Bienvenidos

¡Espero que el contentido sea de tu agrado!

Tutorial Corona SDK

Iniciate en Corona SDK para crear aplicaciones y juegos impresionantes en multiples plataformas con rápidez tanto en el aprendizaje como el desarrollo.

CoreData: Compartir Modelo con el Watch Apple

En este tutorial se ve una introducción a CoreData y posteriormente como se puede crear el modelo para que este compartirdo por diferentes entornos, por ejemplo para la extensión para el Watch Apple.

Lector de Códigos de Barras y QR

El objetivo de este tutorial será ver como se implementa un lector de códigos haciendo uso de AVFoundation en Swift.

SpriteKit con Swift

Adentrate en el mundo del desarrollo de videojuegos con SpriteKit

martes, 21 de abril de 2015

Lector de Código de Barras y QR


1. Introducción

Con la entrada de Swift y iOS 8 son muchas las novedades que han introducido en framework como MapKit, CoreLocation, PassKit o AVFoundation. 

En este tutorial quiero mostraros como realizar un lector de códigos de barras y códigos QR haciendo uso de la librería AVFoundation todo ello haciendo uso de Swift. 


2.¿Qué es un código de barras? ¿y un código de QR?

Aunque muchos de nosotros lo sepamos y veamos a diario siempre es bueno profundizar en algo que vamos a usar. Por ello vamos a hablar de los mecanismos y características escondidas de estos códigos. 

Empecemos por los códigos de barras. Son códigos basado en la representación mediante líneas paralelas de distinto grosor y espaciado de modo que en conjunto contienen una determinada información. Es decir las barras y los espacios representan pequeñas cadenas de caracteres. De este modo son muy usados para recocer rápidamente un artículo de forma única, global y no ambigua.

Existen multitud de tipos como ha ido evolucionando a lo largo del tiempo desde la primera patente en 1952. Gracias a los códigos de barras se ha podido agilizar el etiquetado de precios, el control del stock de mercancías, ofrecer estadísticas comerciales y  automatizar el registro y seguimiento de productos. Todo ello a un bajo coste, con una probabilidad de error muy baja y ofreciendo flexibilidad y rapidez.


Fig. 1 Ejemplo de código de barras


Con respecto a los códigos QR estos son como una evolución de los simples códigos de barras. Se pueden definir como un código de barras bidimensional. Exactamente son una forma de guardar información en un matriz de puntos. Se caracterizan por tener unos cuadrados en las esquinas para detectar la posición del código al lector. Su nombre QR viene de "Quick Response", es decir su objetivo es leer su contenido a alta velocidad.

Fig. 2 Ejemplo de código QR


Para más información ver:

En el siguiente gráfico os muestro algunos ejemplos. Aunque algunos parecen similares son códigos y estándares diferentes.





 Por curiosidad os dejo una página web donde podéis crear vuestro propio código. En mi caso cree uno QR con la cadena "HolaMundo" como información codificada.

Fig. 3 Código QR: HolaMundo

Enlace: GeneradorCódigos


3. Leer Códigos usando AVFoundation

La librería gracias a funcionalidad implementa en la clase AVCaptureMetaDataOutput permite la detección distintos tipos de códigos. La siguiente lista muestra todos los disponibles en la versión 8 de iOS:


      • AVMetadataObjectTypeUPCECode
      • AVMetadataObjectTypeCode39Code
      • AVMetadataObjectTypeCode39Mod43Code
      • AVMetadataObjectTypeEAN13Code
      • AVMetadataObjectTypeEAN8Code
      • AVMetadataObjectTypeCode93Code
      • AVMetadataObjectTypeCode128Code
      • AVMetadataObjectTypePDF417Code
      • AVMetadataObjectTypeQRCode
      • AVMetadataObjectTypeAztecCode
      • AVMetadataObjectTypeInterleaved2of5Code
      • AVMetadataObjectTypeITF14Code
      • AVMetadataObjectTypeDataMatrixCode


Esta funcionalidad de detección se produce en tiempo real mediante la cámara del dispositivo, a través del stream o fotogramas que esta genera.

Para implementar nuestro escaner se necesario crear una sesión (AVCaptureSession) e implementar un método de su delegado. Este proceso se puede complicar en función de lo que busquemos que realice nuestro escáner. En el ejemplo explicaremos lo esencial para un uso adecuado. El ejemplo consistirá en crear un ScannerViewController es decir un controlador de vista que implemente el lector de código de barras permitiendo una funcionalidad y comunicación mediante delegación.

A continuación explicaremos las principales clases de las que haremos uso:


3.1 AVCaptureSession

Se trata de uno de los objetos claves el cual ayuda a manejar el flujo de datos desde  la etapa de captura a través de el dispositivo de entrada como una cámara o micrófono a la salida como puede ser un archivo de audio o video.

3.2 AVCaptureDevice

Representa al dispositivo físico de captura y las propiedades asociadas a este. Este objeto se usa para configurar las propiedades subyacente al hardware. El dispositivo de captura provee la entrada de datos a AVCaptureSession. Se puede tener mucha flexibilidad cambiando las propiedades de entrada como el enfoque o exposición. 

3.3 AVCaptureInputDevice

Es muy util para captura de datos de dispositivos de entrada. 

3.4 AVCaptureMetadataOutput

Es la clase encargada de interceptar objetos de metadatos de parte de la sesión y enviárselos al delegado. Se puede usar procesar específicos tipos de metadatos incluido los datos de entrada. 

3.5 AVCaptureVideoPreviewLayer

Es una especial CGLayer que ayuda a mostrar datos capturar desde dispositivos de entrada.




4. Clase: ScannerViewController

En este apartado vamos a crear nuestra clase para la lectura de códigos de barras y QR. Explicaremos el código relacionado con respecto al lector de códigos en sí ya que es lo más importante. El resto como voy a dejar la clase para descargarla no hay ningún problema, aún así hay unos detalles que comentaré.

Lo primero es ver las propiedades de la clase. La primera de ella es la referida al delegado que explicaremos más adelante. Las siguientes son todas las relacionadas con la captura. Y por último añadimos algunas de control y el código leído en sí.

    var delegate: ScannerViewControllerDelegate?
    
    var device: AVCaptureDevice?
    var input: AVCaptureDeviceInput?
    var output: AVCaptureMetadataOutput?
    var session: AVCaptureSession?
    var preview: AVCaptureVideoPreviewLayer?
    
    var codeDetected: Bool = false
    var code:String?

    var canBeDisplayed = true

A continuación vamos a analizar la función para la puesta a punto de la cámara por partes. La primera de ella que tenemos abajo es comprobar si el dispositivo cuenta con la cámara. Por ejemplo el emulador no tiene y por lo tanto no podemos probar el lector en él.

        self.device = AVCaptureDevice.
                      defaultDeviceWithMediaType(AVMediaTypeVideo)
        if self.device == nil {
            println("No video camera on this device!")
            //self.dismissViewControllerAnimated(true, completion: nil)
            self.canBeDisplayed = false
            return

        }

En el siguiente paso configuramos el resto de instancias. Lo primero es crear la sesión en caso de que no lo este. A continuación creamos y añadimos la instancia de AVCaptureDeviceInput referida a esa sesión.

El siguiente paso es configurar AVCaptureMetadataOutput conectando a la sesión creada e indicando los tipos de códigos permitidos así como indicando su delegado que será este propio controlador. Por último creamos la capa que mostrara los datos para que ocupe toda la vista. Una vez esta todo correctamente configurados podemos lanzar la sesión con "startRunning()".

        if let s = self.session{
            return
            
        }else{
            self.session = AVCaptureSession()
            
            if let s = self.session{
                
                self.input = AVCaptureDeviceInput.
                             deviceInputWithDevice(self.device, error: nil)
                             as? AVCaptureDeviceInput
                
                if s.canAddInput(self.input) {
                    s.addInput(self.input)
                }
                
                self.output = AVCaptureMetadataOutput()
                self.output?.setMetadataObjectsDelegate(self
                             queue: dispatch_get_main_queue())
                if s.canAddOutput(self.output) {
                    s.addOutput(self.output)
                }
                self.output?.metadataObjectTypes = self.output?.availableMetadataObjectTypes
                
                
                self.preview = AVCaptureVideoPreviewLayer(session: s)
                self.preview?.videoGravity = AVLayerVideoGravityResizeAspectFill
                self.preview?.frame = self.view.frame
                self.view.layer.insertSublayer(self.preview, atIndex: 0)
                
                s.startRunning()
            }

        }



Con esto tenemos implementado nuestro método "setupCamera()". A continuación es necesario implementar el método del protocolo AVCaptureMetadaOutputDelegate. Para ello lo primero es añadirlo en la cabecera.  Este método se llama cada vez que se ha detectada un código.

// MARK:  AVCaptureMetadataOutputObjectsDelegate
    func captureOutput(captureOutput: AVCaptureOutput!, 
                       didOutputMetadataObjects metadataObjects: [AnyObject]!, 
                       fromConnection connection: AVCaptureConnection!) {
        
        if !self.codeDetected{
            
            for data in metadataObjects {
                let metaData = data as! AVMetadataObject
                let transformed = 
                    self.preview?.transformedMetadataObjectForMetadataObject(metaData) 
                    as? AVMetadataMachineReadableCodeObject
                
                if let unwraped = transformed {
                    let code: String = unwraped.stringValue
                    println("CodeBar: " + code)
                    if !(code == ""){
                        self.codeDetected = true
                        self.code = code
                        //self.delegate?.codeDetected(code)
                        
                        //self.dismissViewControllerAnimated(true, completion: nil)
                        self.showAlertCodeDetected(code)
                    }
                }
            }
            
        }

    }

Lo siguiente a mostrar de la clase son el propio protocolo delegado que se crea para el controlador, es decir el protocolo que deberán implementar las clases que hagan uso de nuestro escáner. Y por otro lado funciones y detalles correspondientes a detalles de visualización como pueden ser alertas.

Lo primero es definir el protocolo fuera de la clase relacionado con la propiedad delegate de la clase que al principio se comenta:

     protocol ScannerViewControllerDelegate {
    
        func codeDetected(code: String)
    

     }

Las siguientes funciones son simples funciones para mostrar alertas:

    func showAlertError(){
        
        var alertView = UIAlertView(
            title:"Atention",
            message:"Scanner can't be displayed",
            delegate:self,
            cancelButtonTitle:"OK")
        alertView.tag = 1
        
        alertView.show()
    }
    
    func showAlertCodeDetected(code: String){
        
        var alertView = UIAlertView(
            title:"Code Detected",
            message:"The code is: " + code,
            delegate:self,
            cancelButtonTitle:"Accept",
            otherButtonTitles: "Cancel")
        
        alertView.tag = 0
        
        alertView.show()

    }

Y referidas a estas alertas también se implementa su protocolo denominado UIAlertViewDelegate:

    // MARK:  UIAlertViewDelegate
    
    func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
        
        if(alertView.tag == 0){
            
            if buttonIndex == 0{
                self.delegate?.codeDetected(self.code!)
                self.dismissViewControllerAnimated(true, completion: nil)
            }else if buttonIndex == 1{
                
                self.codeDetected = false
            }
            
            
        }else if(alertView.tag == 1){
        
            self.dismissViewControllerAnimated(true, completion: nil)
        }
    }


Por último añado un botón "Cancelar" a la vista para que en caso de no querer detectar ningún código se pueda pulsar y quitar el escáner. Ese código se puede ver en la propia clase ya que no incumbe al tema del tutorial.

En la siguiente imagen se ve un ejemplo donde se detecta un código de barras. Cuando se detecta muestra una alerta y si aceptamos se llama al método "codeDetected" del protocolo ScannerViewControllerDelegate que estará implementado en la clase que hace uso del escáner recibiendo el código.



Aquí dejo los enlaces tanto de descarga directa como de GitHub.

Enlace descarga directa: ScannerViewController
Enlace GitHub: ScannerViewController


lunes, 13 de abril de 2015

¡¡¡ Yovo comparte fotos de forma segura !!!

Los usuarios cada vez hacen más uso de las redes sociales para compartir contenido siendo las fotos de lo que más. Al mismo tiempo los usuarios quieren mantener su privacidad cuando envíen un contenido a una persona únicamente. 
No sirve de nada enviar una fotografía temporal la cual se elimina al tiempo si el otro usuario puede hacer una captura o copia de ella. 
Pues os presento a la aplicación Yovo que descubrí hace poco. Se trata de una aplicación de fotografía que incluye un particular filtro para privacidad a las fotos. El filtro consiste en una rejilla a alta velocidad que permite visualizarla pero impide hacer capturas. En la imagen inferior vemos un ejemplo:


Actualmente solo está disponible para iOS pero dentro de poco se espera una actualización con novedades y su versión Android.
Página oficial de Yovo: https://yovo.me

jueves, 2 de abril de 2015

CoreData: Compartir Modelo con Apple Watch

En este tutorial vamos a ver como una de las posibilidad que iOS ofrece para la persistencia de datos. Hablaré desde mi experiencia, y resolviendo los problemas que me he encontrado yo al usarla en mis aplicaciones con el objetivo de que si habéis dado con este tutorial os evite cometer mis errores al menos. Exactamente al final trataré como solucione un problema que me encontré a la hora de desarrollar la aplicación para el Apple Watch.


1. Introducción

Dentro de iOS encontramos múltiples métodos para la persistencia de datos. Entre ellos encontramos NSUserDefaults o mediante archivos del tipo Property List. Estos métodos resultan más que suficientes para la mayoría de aplicaciones pero tienen ciertas carencias. Las carencias de las que hablo son que permite trabajar con pequeños volúmenes de información, sin relaciones o dependencias y no permite para nada la realización de consultas.

Para aplicaciones que necesiten algún de estas funciones se nos ofrece CoreData.


2. Core Data


Core Data no es una simple base de datos sino que va mucho allá. Esta formado por una serie de clases, que en conjunto unas con otras, conforman un framework para persistencia avanzada.



Por ellos nos permitiría con muy poco esfuerzo crear modelos de datos, relaciones, validación de datos, y posibilidades de deshacer, rehacer y carga inteligente para mejorar el rendimiento de búsquedas. El trabajo que lleva acabo Core Data esta divido en 3 conceptos:


  • Modelado de Datos
  • Persistencia de los datos o guardar.
  • Recuperar o leer la información

Las 3 clases o elementos principales que conforman CoreData son:

  • NSManagedObjectModel: es el modelo de los datos, que propiedades tiene cada objeto, relaciones entre ellos ... Se ve representando como un archivo con extensión .xcdatamodeld.
  • NSManagedObjectContext: es el encargado de la lectura y escritura. Por ello es el más usado ya que nos da acceso a la creación de información así como realizar consultas.
  • NSPersistentStoreCoordinator: es que guarda en disco la información a través de una base de datos, por defecto SQLite.
En el tutorial veremos como se usa cada una de ellas pero nos centraremos en los  2 primeras por que la 3 es muy avanzada y de bajo nivel que en la mayoría de los casos no hay que modificar. 

3. Core Data en mi App

CoreData se puede añadir al crear un proyecto ya que se ofrece la plantilla, muy recomendable usar esta opción al menos para empezar. Y por otra parte se puede añadir a un proyecto ya existente con más que crear el fichero ".xddatamodeld" e crear las instancias de las 3 clases que hemos hablado antes. Ahora iremos entendiendo esto con un pequeño ejemplo. 

Vamos a irnos al a ventana de Xcode para crear un nuevo proyecto y seleccionamos Master-Detail Application,  y veremos que se nos permite poner que queremos usar CoreData. Por otra parte usaremos como lenguaje Swift. En caso de no conocer este lenguaje recomiendo mirarlo un poco antes. 



En caso de que se nos olvide darle a CoreData o ya tengamos una aplicación existente no hay que preocuparse. Xcode lo que hace es crear ciertas variables, archivos y configuración automáticamente para simplificarnos el trabajo. En caso de que se nos olvide pues tendremos que hacer:


  • Crear el archivo .xcdatamodeld
  • Añadir las variables y métodos en la clase AppDelegate (u otra aunque es recomendable aquí)



El nombre del  proyecto que he elegido es MyLeague, este proyecto lo usaré como ejemplo para ir desarrollando el tutorial. Una vez puesto esto y el resto tal y como la figura pasemos a analizar que código a generado automáticamente Xcode



Para empezar veremos la clase AppDelegate.swift para ver las propiedades y métodos creados. Bajando encontraremos las propiedades "lazy", estas son propiedades que se calcula su valor en el momento de que haga falta usarla para así mejorar el rendimiento. Se igualan a un bloque que devuelve una instancia de clase del tipo de la propiedad. 



El primero de ellos variable se denomina applicationDocumentsDirectory y es de tipo NSURL. Como su nombre indica es la url al directorio donde la aplicación guarda y almacena su información.




La propiedad managedObjectModel se encarga de apuntar al archivo que tenemos ..xcdatamodeld sencillamente. Como vemos sencillamente obtiene la URL que identifica al archivo y con ella crea la clase.



Esta propiedad, persistentStoreCoordinator, puede ser la más confusa, pero no es tanto. Se crea a partir del modelo y a continuación lo que hace es configurar que tipo de base de datos usará para realizar la persistencia. Como vemos será de tipo SQLite.


La siguiente propiedad denominado managedObjectContext es con la que más se trabaja como hemos comentado anteriormente. Su creación es sencilla solamente la inicializamos y le asignamos el coordinador que acabamos de ver.


Por último se crea una función que sencillamente realiza la persistencia de los datos si el contexto tiene cambios.



Para terminar con esta parte vamos a ver el archivo MyLeague.xcdatamodeld. Este fichero  almacena el modelo de datos que creemos. Veremos que para simplificarnos la vida Xcode tiene un entorno gráfico para crear el modelo permitiendo añadir:

  • Entidades y sus atributos
  • Relaciones 
  • Peticiones predefinidas (estas no las veremos ya que para empezar no es muy necesario)
En la siguiente imagen podemos ver el entorno gráfico para crear el modelo. En el he creado dos entidades Player y Team (jugador y equipo). 
El jugador tiene una atributo denominado "name", que es el nombre y una relación que indica al equipo que pertenece. Por su parte el equipo tiene un atributo y igual y una relación que indican que jugadores pertenecen al equipo. 

Los atributos pueden de cualquier tipo que aparece en la imagen inferior:


A parte del tipo de atributo se permite modificar diferentes propiedades como valor por defecto, y otros aspectos más avanzados que no comentaré como son "transient", "optional" o "indexed". En la imagen inferior se ve la interfaz que aparece para el caso de un atributo de tipo "string".


En cambio las relaciones son algo más complicadas ya que se indican de que entidad a que entidad van y que propiedad es su inversa. Ejemplo un jugador tiene una relación con un equipo (relación de uno a uno) pero un equipo tiene muchos jugadores (relación de uno a muchos). También puede haber relaciones de muchos a muchos. En el cuadro inferior se ve la interfaz que se abre cuando queremos modificar una relación en sí.




A parte de esto en el proyecto he realizado algunas modificaciones para que añada jugadores con nombre la fecha actual, una tontería pero válido para ir explicando.


Si ahora mismo nos fijamos en el proyecto que tenemos veremos que no tenemos clase asociada a la entidad "Player" o "Team". Esto ocurre por que realmente no es necesario crear subclases de NSManagedObject para poder trabajar con CoreData. Se puede trabajar con estas directamente accediendo a sus atributos a través de claves lo cual esta muy bien pero mucho mejor es referirse mediante clases específicas en el código.
Un ejemplo mediante clave sería: 

  • Crea el objeto: 

let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as! NSManagedObject

  • Se modifica la propiedad del objeto

newManagedObject.setValue("Name: " + NSDate().description, forKey: "name")


Para generar las clases relativas al modelo Xcode nos ofrece una opción en el menú Editor denominada Create NSManagedObject Subclass. Una vez le damos y seguimos los pasos nos crea archivos los cuales son las clases para cada entidad. Una vez creados nos vamos al modelo y le decimos que la clase referida a esa entidad es la que se ha creado. Y con eso ya tendremos listo. 

Ahora se puede realizar así:

  • Crea el objeto: 
let newManagedObject:Player NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) asPlayer


  • Se modifica la propiedad del objeto
newManagedObject.name = "Name: " NSDate().description

Vemos que es mucho más limpio. Además en las clases se puede añadir funcionalidad adicional referida a cada entidad. 


Una vez llegados hasta aquí voy a dejar el proyecto hasta este punto y me voy a centrar en detalle en cuestión que necesite para mi aplicación.

Os dejo el link del projecto de GitHub: MyLeagueV1.0
Link de descarga directa: MyLeagueV1.0

4. Compartir Core Data: App & extensión Watch App

Hace relativamente poco Apple ha introducido su nuevo dispositivo Apple Watch. Por ello me puse a hacer una pequeña aplicación para hacer lista de la compra, de artículos o lo que uno quiera. El caso es que para ello elegí Core Data. El problema fue cuando quise añadir la extensión para que también tuviera la función para el Apple Watch.

El problema es que no podía acceder desde el entorno de este a la clase que las variables referidas al Core Data. Y aquí explico la solución que encontré al problema. 

Pues bien la idea esta en crear una biblioteca o librería aparte que contenga nuestro modelo de CoreData así como todas las variables y clases necesarias. La clase principal de la librería será un "singleton" es decir una clase con una única instancia de modo que desde el propio entorno para la aplicación se pueda acceder a dicha librería y a la vez desde la extensión para el Apple Watch .

El primer paso será crear la librería en nuestro proyecto para eso desde Xcode seleccionamos File --> New --> Target, y se abrirá un menú en el cual elegimos la opción que aparecen en la imagen inferior.


Una vez se crea la librería podemos añadir en ella todo el código correspondiente a CoreData. 

Veamos mi ejemplo para ilustrar como se haría. En mi caso he nombrado al "Framework" como "DateServerKit". En él he los siguientes archivos:

  • DataAccess.swift: es la clase principal, se trata de un "singleton" por el cual podremos acceder desde ambos entornos al contexto y demás elementos de CoreData
  • iwshopping.xcdatamodeld: archivo que contiene el modelo de mi aplicación, en este caso se trataba de un aplicación para hacer lista de artículos
  • Item.swift y List.swift: clases autogeneradas con Xcode referidas al modelo simplemente. 



Analicemos la clase DataAccess ya que es la que tiene más complejidad para crearla. Lo primero que vamos a analizar es como hacer esta clase un "singleton". Existen varias formas iguales de válidas pero yo me he decantado por la que Apple aconseja:

public class var sharedInstance: DataAccess {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: DataAccess? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = DataAccess()
        }
        return Static.instance!
    }

El "singleton" es un método público de clase por el cual siempre accederemos a la misma y única instancia de dicha clase. 

El siguiente paso a tener en cuenta es que debemos definir un App groups (con la cuenta de apple en su página web para desarrolladores) el cual define un lugar de almacenamiento compartido podríamos decir de modo que tanto la aplicación como la extensión pueden acceder a ese contenedor. 
Para crear el App Groups vamos a la página de Apple Developer y en Certificates, Identifiers & Profiles está el apartado. En la imagen vemos una captura para que quede más claro.

La página para crear el grupo es: AppleDeveloperMemberCenter



 El nombre que se le dan a los App Groups son del tipo group.ejemplo.nombregrupo. En mi caso usé el que se ve en la imagen. 

Una vez hemos tenemos ya el grupo definido es necesario tanto en la aplicación con en la extensión añadirlo en el apartado capacidades. En la imagen lo vemos más claro:



Una vez todo preparado hay que añadir en el "singleton" todo la lógica de CoreData que hemos explicado al principio añadiendo una novedad referida al grupo.

En el método que indica la dirección al contenedor donde se almacena la información en este caso debemos tener en cuenta al grupo.



Los grupos también sirve para usar la clase NSUserDefault para guardar determinados valores e información entre las aplicaciones.  Un ejemplo de uso:

var defaults: NSUserDefaults? = NSUserDefaults(suiteName: "group.com.SaulMorenoAbril.iWShopping")

Una vez lo obtenemos ya se puede acceder y escribir valores. 


Para terminar dejo mi framework: DateServerKit