Webservices y JSON con Youtube API. Parte 2: Adapter Pattern

En esta segunda parte te mostraré como usar el patrón adaptador para facilitar el uso de objetos JSON (Diccionarios y arrays). El patrón adaptador es un patrón de diseño que te permite usar instancias de una clase como si fueran otras, es decir encapsula el significado de la clase y actúa como interfaz hacia afuera.

Bájate el código desde github.

Si aún no has configurado tu proyecto, checa la primera parte, donde configuramos el MVC y explicamos la arquitectura.

Agreguemos una clase SearchItem.

Ahora si lo interesante de esta segunda parte, el adaptador. El objetivo de este adaptador es exponer como propiedades los objetos contenidos en el diccionario, para que en lugar de tener diccionarios y llaves regados por todo el código, estos estarán contenidos en el adaptador, que solo los mostrará hacia afuera como las propiedades.

La clase SearchItem representará un solo resultado de la búsqueda. Entonces crea una nueva clase, en Objective-C lo agregaremos como una subclase de NSObject y en Swift lo agregaremos como clase anidada de YoutubeClient:

El objetivo de la clase SearchItem es encapsular los datos que obtendremos del webservice, traduciéndolos de JSON a un objeto nativo, una muestra de un resultado en JSON sería algo como esto:

{
    "kind": "youtube#searchResult",
    "etag": "\"PSjn-HSKiX6orvNhGZvglLI2lvk/otIrgrwhRy2Ap4z3JppVBTPquf4\"",
    "id": {
        "kind": "youtube#video",
        "videoId": "sOnqjkJTMaA"
    },
    "snippet": {
        "publishedAt": "2009-10-03T06:51:49.000Z",
        "channelId": "UCulYu1HEIa7f70L2lYZWHOw",
        "title": "Michael Jackson - Thriller",
        "description": "Music video by Michael Jackson performing Thriller. (C) 1982 MJJ Productions Inc. #VEVOCertified on October 29, 2010. http://www.vevo.com/certified ...",
        "thumbnails": {
            "default": {
                "url": "https://i.ytimg.com/vi/sOnqjkJTMaA/default.jpg"
            },
            "medium": {
                "url": "https://i.ytimg.com/vi/sOnqjkJTMaA/mqdefault.jpg"
            },
            "high": {
                "url": "https://i.ytimg.com/vi/sOnqjkJTMaA/hqdefault.jpg"
            }
        },
        "channelTitle": "michaeljacksonVEVO",
        "liveBroadcastContent": "none"
    }
}

Ya sé que podríamos usar el diccionario que nos devuelve la clases NSJSONSerialization para representar los resultados… ya lo sé!, pero no seas huevón, no es buena idea tener llaves del diccionario regadas por todos lados. Si creamos un adaptador el código será más claro y autodocumentado.

En nuestra clase SearchItem nos vamos a concentrar en el contenido de “snippet”  y “id”, que son dos diccionarios; para atacar el problema, vamos a dividir el problema, en Objective-C haremos lo siguiente.

  1. Definir propiedades en la clase SearchItem que nos devuelvan de manera directa y explícita los datos.
  2. Crear un inicializador que nos reciba el diccionario de donde va a extraer los datos.
  3. En lugar de tener las llaves todas regadas, las conservaremos escondidas en constantes en el archivo m.
  4. Preferiremos un modelo huevón, ejem.. Lazy; para los más lentos: o sea que obtendremos el valor cada que sea necesario, no en el momento de la inicialización.

Objc.
SearchItem.h

#import <Foundation/Foundation.h>

@interface SearchItem : NSObject
// Estas propiedades serán visibles hacia afuera, pero serán calculados en el momento que sean solicitadas.
@property (nonatomic,readonly) NSString *videoId;
@property (nonatomic,readonly) NSString *publishedAt;
@property (nonatomic,readonly) NSString *title;
@property (nonatomic,readonly) NSString *videoDescription;
@property (nonatomic,readonly) NSString *defaultThumbnail;
@property (nonatomic,readonly) NSString *mediumThumbnail;
@property (nonatomic,readonly) NSString *highThumbnail;
@property (nonatomic,readonly) NSString *channelTitle;

- (instancetype)initWithDictionary:(NSDictionary*)dictionary;
@end

SearchItem.m

#import "SearchItem.h"
// en lugar de tener cadenas mágicas por todos lados, concentramos las llaves aquí
NSString * const kVideoId = @"videoId";
NSString * const kPublishedAt = @"publishedAt";
NSString * const kTitle = @"title";
NSString * const kVideoDescription = @"description";
NSString * const kThumbnails = @"thumbnails";
NSString * const kChannelTitle = @"channelTitle";
NSString * const kId = @"id";
NSString * const kSnippet = @"snippet";

NSString * const kDefaultThumbnail = @"default";
NSString * const kMediumThumbnail = @"medium";
NSString * const kHighThumbnail = @"high";
NSString * const kURL = @"url";

@interface SearchItem ()
@property (strong,nonatomic) NSDictionary *dictionary;
@end

@implementation SearchItem
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
  if (self = [super init]) {
    self.dictionary = dictionary;
  }
  return self;
}

- (NSDictionary*)snippet {
  return self.dictionary[kSnippet];
}
- (NSDictionary*)_id {
  return self.dictionary[kId];
}
- (NSDictionary*)thumbnails {
  return [self snippet][kThumbnails];
}

- (NSString*)videoId {
  return [self _id][kVideoId];
}

- (NSString*)publishedAt {
  return [self snippet][kPublishedAt];
}

- (NSString*)title {
  return [self snippet][kTitle];
}

- (NSString*)videoDescription {
  return [self snippet][kVideoDescription];
}

- (NSString*)channelTitle {
  return [self snippet][kChannelTitle];
}

- (NSString*)defaultThumbnail {
  return [self thumbnails][kDefaultThumbnail];
}

- (NSString*)mediumThumbnail {
  return [self thumbnails][kMediumThumbnail];
}

- (NSString*)highThumbnail {
  return [self thumbnails][kHighThumbnail];
}
@end

Swift
Para Swift vamos a seguir una estrategia similar, pero aprovechando algunas características de Swift:

  1. Anidaremos la clase SearchItem en YoutubeClient para establecer la jerarquía de clases y limitar su scope.
  2. Como las constantes de las llaves solo serán usadas aquí, también las anidaremos en un struct privado.
  3. Los thumbnail los encapsularemos en un miembro de SearchItem y lo anidaremos dentro de la misma clase de nuevo con el objetivo de limitar su scope a donde es necesario, esto es algo que extrañaba en Objective-C, la habilidad de crear namespaces.
  4. Al igual que en Objective-C solo expondremos al exterior las propiedades, que también serán Lazy, como tú comprenderás.
class YoutubeClient {
  // Usa el anidado de clases para crear una jerarquía de clases, o namespace
  class SearchItem {
    // una de las ventajas de anidar clases y structs es que puedes clasificarlos y reducir su scope
    private struct Constants {
      static let videoId = "videoId"
      static let publishedAt = "publishedAt"
      static let title = "title"
      static let videoDescription = "description"
      static let thumbnails = "thumbnails"
      static let channelTitle = "channelTitle"
      static let id = "id"
      static let snippet = "snippet"
    }
    class Thumbnails {
      private struct Constants {
        static let defaultThumbnail = "default"
        static let mediumThumbnail = "medium"
        static let highThumbnail = "high"
        static let url = "url"
      }
      private func thumbnailWithName(name:String) -> String? {
        if let thumbnail = self.dictionary[name] as? [String: AnyObject] {
          return thumbnail[Constants.url] as String?
        }
        return nil
      }
      private let dictionary: [String:AnyObject]!
      var defaultThumbnail: String? {
        get {
          return thumbnailWithName(Constants.defaultThumbnail)
        }
      }
      var mediumThumbnail: String? {
        get {
          return thumbnailWithName(Constants.mediumThumbnail)
        }
      }
      var highThumbnail: String? {
        get {
          return thumbnailWithName(Constants.highThumbnail)
        }
      }
      init(dictionary: [String:AnyObject]) {
        self.dictionary = dictionary
      }
    }
    
    private func snippetPropertyWithName(name: String) -> AnyObject? {
      if let snippet = self.dictionary[Constants.snippet] as? [String: AnyObject] {
        return snippet[name]
      }
      return nil
    }
    
    var videoId: String? {
      get {
        if let id = self.dictionary[Constants.id] as? [String: AnyObject] {
          return id[Constants.videoId] as String?
        }
        return nil
      }
    }
    var publishedAt: String? {
      get {
        return snippetPropertyWithName(Constants.publishedAt) as String?
      }
    }
    var title: String? {
      get {
        return snippetPropertyWithName(Constants.title) as String?
      }
    }
    var videoDescription: String? {
      get {
        return snippetPropertyWithName(Constants.videoDescription) as String?
      }
    }
    var channelTitle: String? {
      get {
        return snippetPropertyWithName(Constants.channelTitle) as String?
      }
    }
    
    let thumbnails: Thumbnails?
    
    private let dictionary: [String:AnyObject]!
    init (dictionary: [String:AnyObject]) {
      self.dictionary = dictionary
      if let thumbnailDictionary = self.snippetPropertyWithName(Constants.thumbnails) as [String:AnyObject]? {
        self.thumbnails = Thumbnails(dictionary: thumbnailDictionary)
      }
    }
  }
}

Checa el tutorial de variables y constantes en swift si tienes alguna duda de los opcionales en swift, o si de plano eres nuevo checa el tutorial para Aprender swift en 7 minutos, garantizado!!.

En la siguiente parte haremos las llamadas al webservice para obtener los datos en formato JSON.

Webservices y JSON con Youtube API. Parte 1: Arquitectura y MVC

En este tutorial te mostraré cómo usar Webservices que te devuelven JSON, como lo son todas las API nuevas de Google, en este caso usaré la API de Youtube para realizar búsquedas y mostrar los resultados en un Table View. El código lo veremos al mismo tiempo en Objective-C y en Swift (chécate el tutorial para Aprender a programar en Swift en 7 minutos!).

Bájate el código desde github.

En la actualidad, muchas apps bajan contenido de internet, aunque hay muchos formatos que pueden usar, como XML, archivos de texto planos, archivos CSV, etc; sin duda uno de los más usados es el JSON (Se dice “Yeison”, no me salgas con la mamada de “JOTASÓN”, asco!!), probablemente por su simpleza.

Un archivo de JSON, es un archivo de texto que suelen contener en su estructura diccionarios o arrays o valores, que a su vez pueden contener otros diccionarios, arrays o valores; muchas APIs en la actualidad usan JSON; para este ejemplo usaremos la API V3 de Youtube.

En esta primera parte te mostraré como configurar el proyecto en ambos lenguajes, Swift y Objective-C. También crearemos la estructura básica del MVC.

En la segunda parte usaremos el patrón adaptador para convertir los diccionarios y arrays obtenidos desde el webservice para encapsular la responsabilidad de la conversión.
Ir a la segunda parte -> Adapter Pattern.

En la tercera parte te mostraré como hacer llamadas y consumir los servicios de manera asíncrona al webservice.

En la parte final, conectaremos todos los componentes con la UI para mostrar los resultados.

Ok Caon, Comencemos.

Primero sigue los pasos que se indican en la página de la API V3 de Youtube; yo te esperaré tomando un café de java… oh no espera no tomo café, quise decir Cocoa, anda y ve…

¿Listo? Para ahora lo importante es que tengas tu API-KEY.

Arquitectura.

La arquitectura para esta App es muy sencilla

Captura de pantalla 2014-10-15 a la(s) 21.17.00

Para la comunicación entre Controlador y Modelo lo voy a resolver con el patrón delegado, para conservar uniformidad entre Swift y Objc, ya que Swift no implementa el patrón KVO debido a que Swift es type-safe.

Para el Client de Youtube emplearé métodos con callbacks en blocks o closures como se te acomode mejor (Objc, Swift).

Empecemos.

Crear el proyecto.

Usaré Xcode 6.1 que es el release estable al momento de escribir esto, pero los pasos serán similares en la versión que tengas de Xcode.

Empecemos por crear un nuevo Proyecto (File > New > Project), selecciona SingleView.

Ponle de nombre YoutubeSearch, en realidad le puedes poner como se te hinchen los huevos, pero por uniformidad con el código te recomiendo que le pongas así, ok???

Captura de pantalla 2014-10-15 a la(s) 21.25.01

En lenguaje escoge el lenguaje que quieras, en el tutorial veremos los dos, o sea que el que mejor te entre. En Devices selecciona solo iPhone para simplificar el ejemplo y desmarca la casilla de Core Data, en este tutorial todo lo haremos de memoria ;)

Configuración del MVC.

Como probablemente ya sabes MVC es el modelo favorecido para las Apps de iOS, pronto escribiré un tutorial sobre él, mientras lo que tienes que saber es:

  • El Modelo realiza cálculos sobre los datos, almacena, etc, es decir es la parte “inteligente” del patrón.
  • El controlador da ordenes y recibe retroalimentación.
  • La vista es estúpida, solo lo que sabe es verse bonita y avisar al controlador que hay cambios en la interfaz.

Lo primero que hay que hacer es cambiar nuestro controlador para que sea una subclase de UITableViewController.

Objective-C: ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController

@end
Swift: ViewController.swift

import UIKit

class ViewController: UITableViewController {

// ... no pondré todo el código

A continuación modificaremos el Storyboard, esta parte es igual para Ambos lenguajes. Abre tu Main.storyboard:

1. Elimina el controlador creado por defecto (View Controller)

Captura de pantalla 2014-10-15 a la(s) 21.39.49

2. Busca el Table View controller desde la barra de la derecha y arrástralo hacia el storyboard.

Captura de pantalla 2014-10-15 a la(s) 21.42.28

3. Selecciona el nuevo Table View Controller del Document Outline:

Captura de pantalla 2014-10-15 a la(s) 21.49.30

4. Selecciona el Identity Inspector en la barra de Utilities y en el Campo de Class escribe ViewController (Es nuestro controller)

Captura de pantalla 2014-10-15 a la(s) 21.44.35

 

5. Busca la Search Bar en la Object Library y arrástrala sobre la table view en el storyboard para agregarla como subview.

Captura de pantalla 2014-10-15 a la(s) 22.31.23

6. Selecciona la Search bar y verifica que el data source y el delegate esten conectados al View Controller en el Inspector de Connections, si no, conéctalos.

Captura de pantalla 2014-10-15 a la(s) 22.33.28

7. Ahora ya tenemos nuestro Controller y Storyboard….

Agreguemos el Modelo.

El modelo se encargará de almacenar y solicitar la información al cliente, también notificará al controlador sobre los cambios en los datos.

Agrega un nuevo archivo (File > New > File) y Selecciona Swift File o Cocoa Touch Class (Objective-C), en Objective-C hazlo subclass de NSObject, llámalo ViewModel, obtendrás los siguientes archivos según el lenguaje:

Objc:

ViewModel.h

#import <Foundation/Foundation.h>

@interface ViewModel : NSObject

@end

ViewModel.m

#import "ViewModel.h"

@implementation ViewModel

@end

Swift.

ViewModel.swift

import Foundation
class ViewModel {

}

La clase YoutubeClient

La clase YoutubeClient se encargará de comunicarse de manera asíncrona con el webservice, jalará los datos y los devolverá como instancias del adaptador de resultados de la búsqueda.
Agrega un nuevo archivo (File > New > File) y Selecciona Swift File o Cocoa Touch Class (Objective-C), en Objective-C hazlo subclass de NSObject, llámalo YoutubeClient:

Objc:
YoutubeClient.h

#import <Foundation/Foundation.h>

@interface YoutubeClient : NSObject

@end

YoutubeClient.m

#import "YoutubeClient.h"

@implementation YoutubeClient

@end

Swift

import Foundation
class YoutubeClient {
  
}

OK. Con esto tenemos la estructura básica de la aplicación, en el siguiente tutorial te muestro cómo usar el Patrón Adaptador o en inglis Adapter pattern.

Aprende a programar para iPhone con swift: variables, constantes y tipos de datos

Si ya tienes algo de experiencia de programación puede que quieras leer el tutorial para aprender a programar para iPhone con swift en 7 minutos, en este articulo veremos a detalle las variables y constantes de swift, el nuevo lenguaje de programación de Apple.

Para que aprendas mas rápido y te hagas la vida más fácil, te recomiendo que abras un playground en Xcode 6+ (File > New > Playground), cada que veas un bloque e código como el siguiente podrás escribir (o para los huevones, copia y pegar) el código:

println("Hola mundo")

Variables y constantes

Las variables y constantes representan almacenes de datos, espero que no seas tan lento para darte cuenta que las variables se espera que cambien, y las constantes tendrán el mismo valor en el tiempo que dura dura.

Las variables se declaran usando la palabra var, y las constantes con la palabra let:

var age = 28
let adultAge = 18

Siguiendo las mejores practicas del lenguaje, las variables y constantes deben tener nombres descriptivos que con el simple nombre te digan para que chingaos la creaste, puedes nombrar tus variables y constantes como quieras, pero existen algunas regla y algunas guías:

Los nombres no deben comenzar comenzar con un número, pero pueden contener números en medio o al final, por convención, las variables y constantes comienzan con minúscula:

var NombreValidoPeroNoRecomendable : Tipo
var 123NombreInvalido = Tipo

Los nombres deberían usar camel case, es decir: usarasMayusculaEnElComienzoDeCadaPalabra; en los nombres de las variables en swift puedes usar Unicode, o sea que ya puedes escribir nombres de variables en correcto castellano, tío:

var  nombreExplícitoEnEspañol : Tipo // valid!!
let 你好 = “Hola” // valid!!

Los nombres no pueden contener espacios en blanco, o símbolos matemáticos o separadores.

Declaración

No como declaración de amor no mames, sino es la manera en que creas una nueva variable o constante, en general la sintaxis es la siguiente:

var nombreDeVariable : Tipo
let nombreDeConstante : Tipo

También puedes asignar un valor des el momento de la creación e la variable con la siguiente sintaxis:

var nombreDeVariable = valorInicial
let nombreDeConstante = datoConstante

El contenido de las variables las puedes cambiar cuando se te hinchen las pelotas, pero las constantes no se puede, simplemente el compilador no te dejara.

let name = "Emanuel"
var age = 10 // valor inicial 10
age = 11

Ejercicio. Intenta cambiar la constante nombre

Se pueden declarar muchas variables en una misma línea si las separas con una coma:

var edadInicial = 18, edadFinal = 22

Chingon tip: Si el valor del dato que estas creando no va a cambiar, decláralo como constante, con let, para que sea claro en tu código la intensión.

Anotaciones de tipo.

A menos que seas medio wey, ya notaste que en las declaraciones de variables y constantes puedes indicar el tipo de dato que la variable o constante va a contener, al igual tu novia que te maltrata, chantajea y manipula, el tipo e la variable o va a cambiar:

var day : String // solo podrá contener datos tipo String
day = "lunes" // ok

Ejercicio: nomas pa que me creas intenta asignar un numero entero a la variable day.

Inferencia de datos.

Una e las características que hace que swift este bastante chido es la inferencia de datos, aunque no es algo nuevo, resulta muy útil. La inferencia de datos significa que no tienes que ponerle a patín el tipo de dato que va a contener la variable o la constante, sino que Swift puede deducirlo por el contexto, por ejemplo:

var string = "Soy una variable tipo String"
// Equivale a :
// var string :String = "Soy una variable tipo String"

let integer = 31337
// Equivale a :
// let integer :Int = 31337

var array = ["elemento1","elemento2","elemento3"]
//var array :[String] = ["elemento1","elemento2","elemento3"]

var dictionary = [ "Key1" : "Valor1", "Key2" : "Valor2", "Key3" : "Valor3"]
//var dictionary :[String:String] = [ "Key1" : "Valor1", "Key2" : "Valor2", "Key3" : "Valor3"]

Por eso se dice que Swift es un lenguaje “type-safe“, o sea que Swift sabe de que tipo son las variables, y que tipo de valor devuelven las funciones, a diferencia de Objective-C donde los chequeos de tipo se hacían realmente en la ejecución, aunque el compilador hacía un chequeo superficial.

Conversiones de tipos.

Para acabar pronto, Swift no hace conversiones de tipo implícitas, nunca, ni siquiera con números, no insistas, no lograrás nada, como con tu ex que no has logrado olvidar.

Observa los siguientes ejemplos:

var implicitInteger = 1300
var implicitDouble = 37.0

var sum = Int(implicitDouble) + implicitInteger

var descriptionLabel = "Hola, soy "
var description = descriptionLabel + String(sum)

Try this: Intenta quitar las palabras Int, y String de las declaraciones en la variable sum y description. ¿Que pasa morro?

Recuerda, Swift es “Type Safe“, si no eres su tipo, no te va a querer, como la vecina de la que has estado enamorado desde los 12 años.

Podría parecer que en la línea:

var description = descriptionLabel + String(sum)

Simplemente se esta haciendo un cast, pero no es así, en realidad NO ES UN CAST. Lo que en realidad esta sucediendo es que estas creando una nueva instancia de String, ya que el operador “+” se encuentra sobrecargado para (String, String) -> String, o sea, tomar dos String y devolver otro, como nota cultural, en Swift puedes crear tus propios operadores.

El hecho de que Swift pueda checar el tipo de dato que contienen las variables y las constantes, significa menos errores en el tiempo de ejecución, aunque también como todo en la vida, hay un tradeoff, a cambio de menor flexibilidad.

Type Alias.

Swift al igual que otros lenguajes te permite crear alias para los diferentes tipos, puedes pensar en los alias como un typedef, o alias, de otros lenguajes, ¿Que pa qué sirve?, aaaa pinche muchacho resongón, pues sirven para darle un nombre más apropiado, o para ahorrarte unos teclazos:

typealias TimeInterval = Int
var timeInterval : TimeInterval = 60 // 1 Minuto

OK, en este caso escribiste más lineas que si hubieras escrito solo Int, o más aún de haber usado la inferencia de datos, pero ya no eres un puberto preparatoriano, si quieres hacer Ingeniería de software debes dejar la hueva de lado, anda ve y cuélgala y regresas…

Ya? OK. El chiste de esto es hacer el código más legible, autodocumentado.

Tuples

Las tuples son un tipo de dato en Swift que te permite agrupar dos o más valores en uno solo, lo cual te sirve para ahorrarte variables adicionales o estructuras de datos que solo usas en un sitio y ya jamás los vuelves a usar, lo cual no esta chico, no es buena ingeniería bato.

Las tuples las puedes crear en cualquier lugar, per o son usadas frecuentemente como retorno de una función:

var gradeArray = [ 100, 90, 96, 98, 100]

func gradeInformationForArray(gradeArray :[Int]) -> (average: Double, gradeCount: Int) {
  var sum = 0
  for grade in gradeArray {
    sum += grade
  }
  return (Double(sum)/Double(gradeArray.count),gradeArray.count)
}

let gradeInformation = gradeInformationForArray(gradeArray)
gradeInformation.0
gradeInformation.average
gradeInformation.1
gradeInformation.gradeCount

Las tuples pueden contener el número de valores que quieras, en este caso contiene un par de valores, el primero de tipo Double, el segundo es un Int. Chécate que al final, se puede hacer referencia a los valores del tuple de dos formas, con el índice (en caso de no haber proporcionado un nombre a la tuple) o con el nombre que se le proporciona en la declaración de la función al tipo de retorno, lo último no es necesario, puedes tener una tuple anónima y usar los índices.

Ejercicio. Quita el nombre de los elementos de la tuple (“average:”, “gradeCount.”) en la declaración de la función,  para que solo sea accesible con el índice.

Optionals.

Si has programado para C, Objective-C, o C++, estarás familiarizado con el concepto de NULL, o nil. Es un patrón común en estos lenguajes checar si el apuntador apunta a nulo para ver si existe un valor o prevenir accesos incorrectos a memoria.

En Swift no puedes asignar null o nil a variables normales, anda si no me crees inténtalo:

var notNullableVariable = "valor inicial"
notNullableVariable = nil

var notAValidDeclaration = nil
var anotherInvalidDeclaration :String = nil

¿ Ya vez?, te dije wey.

Entonces, ¿cómo putas le haces para indicar que no hay un valor?.

Enter the Optionals.

Los optionals no son nada nuevo, lenguajes como C# los soportan, el concepto es el equivalente a una caja que puede o puede no tener un valor, la caja en sí no hace nada, solo es el recipiente para transportar algo (some) o nada (nil):

var imAStringOptional : String? = nil
var imAnotherStringOptional : String? = "Valor"

let invalidParsedInt = "notANumber".toInt() // la cadena no es un número, devolverá nil
let validParsedInt = "31337".toInt() // la cadena puede ser convertida a Int, devolverá { Some 31,337 }

Si me hiciste caso y abriste un archivo de playground podrás ver que en la segunda declaración, el playground te muestra que la primera variable contiene nil y la segunda contiene { Some “Valor” }. También la primera constante devolverá nil, ya que esa cadena no se puede representar como entero, y la segunda constante sí se pude, por lo que te devolverá un opcional con some.

Ejercicio. Intenta asignar nil a imAnotherStringOptional

OK, entonces cómo accedo a los datos dentro de la caja Some. Existen varias formas:

Forced Unwrapping.

Si estas seguro que el optional contiene some, es posible abrir la caja por la fuerza, lo que se conoce como Forced Unwrapping, siguiendo con el ejemplo de los optionals, si el optional es diferente a nil, quiere decir que contiene some, para forzar la extracción, usa el operador unario ! después de la variable opcional para desempaquetarla

if invalidParsedInt != nil {
  println("No you're never gonna get it")
  println(invalidParsedInt!)
}
if validParsedInt != nil {
  println("How are you handsome?")
  println(validParsedInt!)
}

Wachaut: Es muy importante que te asegures que contiene some, ya que si intentas usar el valor del optional cuando contiene nil, podrás incurrir en excepciones de memoria.

Time to break stuff: Intenta desempaquetar el optional invalidParsedInt fuera del scope del if, ¿Qué sucede?

Optional Binding.

Un patrón útil con los optionals es el binding, que consiste en un chequeo y extracción automática en caso de que contenga some.

Volvamos  al ejemplo de los integers parseados.

if let parsedInt = validParsedInt {
  println("Ahora puedes usar el valor de parsedInt: \(parsedInt)")
}

if let parsedInt = invalidParsedInt {
  println("Esta línea no verá la luz del sol: \(parsedInt)")
} else {
  println("No se pudo convertir el número")
}

Con eso realizas el chequeo de some y la extracción del valor, que se hacen en un solo paso.

Opcionales implícitamente extraídos.

Existen casos donde a pesar de que la API te devuelve un optional, uno puede estar seguro que ese optional contiene some, como en el caso de la inicialización de miembros de una clase, que comienzan con un valor inicial, pero en determinado punto pueden volverse nil. Para esos casos podemos usar un opcional explícitamente extraído, que es un opcional que se extrae automáticamente, sin embargo no debes descuidar que si llega a ser nil en el momento de usarlo, te lanzará al infierno de las excepciones en la ejecución.

class Person {
  var name = ""
  var telephoneNumber : String? = nil
  var nacionalidad : String! = "Mexicano"
  func description() {
    println("Nombre :" + self.name)
    if let telephoneNumber = self.telephoneNumber {
      println("Número telefónico: \(telephoneNumber)")
    } else {
      println("\(self.name) valora su privacidad")
    }
    if nacionalidad != nil {
      println("\(self.name) es \(self.nacionalidad)")
    } else {
      println("\(self.name) no tiene nacionalidad")
    }
  }
}

var chucho = Person()
chucho.name = "Chucho"
chucho.telephoneNumber = "3133731337"
chucho.nacionalidad
chucho.description()

var edwardSnowden = Person()
edwardSnowden.name = "Edward"
edwardSnowden.telephoneNumber = nil
edwardSnowden.nacionalidad = nil
edwardSnowden.description()

Aunque normalmente las personas tienen nacionalidad, el pobre Edward fue exiliado, entonces no tiene nacionalidad, lo lógico sería tratar al miembro nacionalidad como una variable no opcional, ya que en la mayoría de los casos sí se tiene, por eso se hace un opcional explícitamente extraído, pero aún así, se tiene que hacer el chequeo, debido a que si intentamos acceder al valor cuando no contiene some sino nil, el programa nos arrojará una excepción del tiempo de ejecución. Not good…

OK, eso es to.. eso es to.. eso es todo amigos.

Si tienen alguna duda o algún tema que les gustaría tratar, no duden en hacérmelo saber.

Aprende a programar para iPhone en Swift en 7 minutos

Si quieres aprender a programar swift en 7 minutos, garantizado, llegaste al lugar indicado. Si no te bastan 7 minutos, te mando los otros 7 minutos gratis!, agárrate bien del caballo, porque nos vamos a ir en chinga, aunque seamos sinceros, si no tienes nada de experiencia en programación… no hago milagros.

Para acelerar tu aprendizaje, te recomiendo que abras Xcode 6+ con un nuevo archivo de playground (File > New > Playground) para que veas en tiempo real que hace cada línea de código en la columna a la derecha del código, y de preferencia pon todo el código en el mismo playground y haz la lectura como dios manda, empieza por arriba y bajate a los chescos.

Nota. En el tutorial voy a mezclar español e inglés en el código, pero solo con fines de diversión, lo mejor es que uses inglés en código serio para darle consistencia al código. OK, comencemos:

Los programas en swift…

No necesitan función main, ni nada parecido, swift va a empezar la ejecución en el código en el scope global. Entonces el siguiente es un código de un programa completo y valido:

println("Hola mundo")

Tampoco es necesario que termines las líneas con punto y coma – felicidades a la bola de huevones – a menos que haya varias sentencias en un mismo renglón:

println("Hola"); println(" mundo")

Variables y constantes.

(Ver artículo principal: Variables, constantes y tipos de datos )

Swift es diferente a objetive-c en como se declaran las variables y constantes, para declarar una variable usas la palabra var, para una constante usas la palabra let:

var tiempo = "4:20"
let nombre: String = "Emanuel"
let apellido = "Peña"
tiempo = "12:00"

Ejercicio: intenta modificar nuevamente el valor de nombre. ¿Qué error te da?

Swift es un lenguaje type-safe:

En swift es un lenguaje tipeado, o se que las variables solo pueden contener un tipo de valor, y no puedes asignarle un tipo distinto.

Ejercicio. Guacha y aprende a ver si entiendes monigote, intenta lo siguiente:

tiempo = 12000 // uy... a poco no te dejó??

Aunque swift es un lenguaje tipeado, no necesitas poner el tipo del dato si das el valor inicial, como en la primera y tercera declaración, porque swift usa inferencia de datos, o sea que observa el tipo del dato que le estas asignando y define la variable con ese tipo, empero, si no le das un valor inicial, debes indicarle un tipo ( var nombre: Tipo)

Swift no hace conversión de tipos automáticamente, nunca de los nunca, entonces debes escribir explícitamente el tipo al que quieres convertir:

let prompt = "mi nombre es " + nombre + ", im " + String(1337) + " dude"

Intenta: remover la palabra String, ¿que error te da?

También puedes crear strings usando la siguiente sintaxis, que se le llama interpolación de cadenas (String interpolation):

let yourDescription = "\(nombre) dice: 1/3 = \( Double(1)/3 )"

Optionals

En swift no puedes asignar null o nil a las variables, entonces la siguiente línea no es valida:

tiempo = nil

Pero el concepto de un apuntador vacío se transformo en los opcionales:

var segundoApellido : String? = nil
segundoApellido = "Aguilar"

Un opcional es como una caja, donde puede haber algo (some) o puede no haber un valor (nil)

Para extraer el valor e un opcional del que estas seguro que sí contiene un valor puedes usar el operador !

println("\(nombre) \(apellido) \(segundoApellido!)")

Ejercicio. Intenta lo siguiente, observa el error que obtienes:

var stringNoOpcional :String = segundoApellido

Arreglos y diccionarios.

Los arreglos y diccionarios al igual que las demás variables o constantes solo te aceptan objetos del tipo que fue creado:

// Array de String
var nombres = ["Ana", "Pedro", "Elvis"] // var nombres :[String]
// Array de Int
var edades = [ 20, 16, 100] // var edades :[Int]
// Diccionario de llaves String, valores Int
var diccionario = ["Juanita": 24] // var diccionario = [String, Int]()

Puedes recorrer los elementos de un array o diccionario usando un ciclo for in con un enumerador del array, o con un intervalo abierto (Explicado también abajo):

for alguien in nombres { // enumerador
  println(alguien)
}

A diferencia de Objc, no se manejan arrays o diccionarios mutables/inmutables, la mutabilidad la controlas en el momento en que defines si es una variable (var) o constante (let). Puedes agregar y eliminar elementos si es que declaraste tus variables como var, de la siguiente manera:

for i in 0 ..< nombres.count { // ciclo for con intervalo abierto de [0,nombres.count)
  let edad = edades[i]
  if edad >= 18 {
    diccionario[nombres[i]] = edad //agrega al diccionario
  }
}
diccionario //mayores de edad
nombres.removeAtIndex(0) //elimina del array
edades.removeAtIndex(0) // elimina del array
diccionario.removeValueForKey("Elvis") //elimina el valor correspondiente del diccionario
diccionario

Ejercicio. En el siguiente snippet quita el comentario de la última línea para intentar agregar un nuevo elemento al array, ¿Qué te alega el playground?

nombres.append("Chuchita")
edades.append(15)
let arrayInmutable = ["Hola", "mundo", "!"]
//arrayInmutable.append("Not gonna happen")

Importante: A diferencia de objective-c, los array y diccionarios SIEMPRE se pasan como copia, no como referencia, esto debido a que internamente se trata de structs, no clases (ver más abajo).

 Ejercicio. Intenta lo siguiente en el playground y observa lo que sucede con cada variable:

var mamiferos = [ "Perro", "Humano", "Caballo" ]
var animales = mamiferos
animales.append("Pollo")
animales += [ "Tortuga", "Tiburón" ]

animales
mamiferos

Control de flujo

Swift soporta las estructuras de control de flujo estándar, if, switch, for, for in, while, do while. Por lo que solo comentaré las diferencias significativas respecto a los lenguajes basados en C; para comenzar los paréntesis en todas las estructuras de control de flujo son opcionales.

if

Como swift no hace conversión de tipos de manera automática, el valor que pases debe ser Boolean de a huevo, entonces no puedes hacer algo como if entero, debes usar un operador que devuelva Boolean, como if entero ¡= 0:

var valor = 1
// Válido:
if valor > 0 {
    println("Línea válida")
}
// inválido
if valor {
    println("No te hagas wey arbitro")
}

La “excepción” es cuando te encuentras con un valor opcional:

if let edadDeElvis = diccionario["Elvis"] { // nil
 edadDeElvis // sorry man, Elvis is dead
} else if let edadDeChuchita = diccionario["Chuchita"] { //some
 // A chuchita la bolsearon
 edadDeChuchita
}

El if desempaqueta automáticamente el opcional si contiene un valor y si es nil, se salta  el bloque de código del if.

switch

Es un switch de C en “asteroides” [sic], te permite introducir rangos, comparar cualquier tipo de dato (mientras sean comparables) y no es necesario que escribas break después de cada caso. Algo que sí es necesario es cubrir todos los casos posibles, para eso te puedes auxiliar de default.

for edad in edades {
    switch edad {
    case 0..<12:
        println("Chamuco")
    case 12..<18:
        println("Puberto")
    case 18..<60:
        println("Godinez")
    case 60..<150:
        println("Ol' timer")
    default:
        println("Are U fakin kiddin me?")
    }
}
for personaje in nombres {
    switch personaje {
    case "Martina":
        println("Quince años tenía martina")
    case "Pedro":
        println("Navajas")
    case "Elvis":
        println("Vive")
    default:
        println("who?")
    }
}

Los casos del switch no continúan en el siguiente case, por eso no es necesario el break. Si quieres que se aplique al siguiente case, debes usar fallthrough: después de cada caso, el siguiente case será aplicado sin consultar la condición.

for in, for

La estructura for in, esta bien chida en swift, puedes usarla como enumerador, soporta rangos, y tuplas.

for c in 0..<edades.count { // intervalo abierto por la derecha [0,edades.count)
    edades[c]
}

Puedes crear intervalos cerrados o abiertos por derecha, los intervalos abiertos usan el operador “..<“, los intervalos cerrados usan el operador “…”

El conocido for vuelve, con pocas diferencias:

for var i = 0; i<edades.count; i++ {
    edades[i]
}

while, do while

No mames son básicas…  no cambian nada en swift a C, JS, Java, Objc, C#… , bueno los paréntesis son opcionales en la condición

Funciones y closures

Recientemente se agregó a objective-c lo que seria el equivalente a las funciones anónimas en otros lenguajes; la verdad la sintaxis de los blocks en obj-c esta de la verga. Lo chido es que en swift tomaron nota y esta mucho más chingon.

Funciones

Las funciones son bastante tradicionales:

func cuadrado(x :Int) -> Int { // func nombre(param :Tipo, param2 :Tipo2) -> TipoRetorno
  return x*x
}
cuadrado(2)
cuadrado(2+3)

Una característica nueva de las funciones en swift son las tuples, que son un nuevo tipo de dato, que puede contener varios valores en uno solo, esto te puede ahorrar estructuras auxiliares:

func mitadCuadradoCubo (x :Int) -> (mitad :Double,cuadrado :Int, cubo :Int) {
  return (Double(x)/2,x*x,x*x*x)
}
var tupla = mitadCuadradoCubo(2)
tupla.0
tupla.mitad
tupla.1
tupla.cuadrado
tupla.2
tupla.cubo
mitadCuadradoCubo(3)

Puedes nombrar a cada miembro de las tuples con una etiqueta descriptiva, o puedes hacer referencia a ellos con el índice del miembro de la tuple

Ejercicio. A ver muchachito haz una función que reciba un array de Double y devuelva una tuple con el promedio y la suma

Las funciones también pueden devolver otras funciones, ya que en lo oscurito, también se trata de una clase, por lo tanto puedes anidar funciones dentro de otras funciones, o recibir funciones como argumentos:

// checa el tipo de retorno (Void -> Int), esto quiere decir que devuelve una función con parámetro (Void) y devuelve un Int
func makeCounter(startAt initialValue:Int, increaseBy increment:Int) -> (Void -> Int) { 
  var startAt = initialValue
  func counter() -> Int {
    startAt += increment
    return startAt
  }
  return counter
}
var countByTwo = makeCounter(startAt: 0, increaseBy: 2)
countByTwo() //2
countByTwo() //4
var countByThree = makeCounter(startAt: 10, increaseBy: 3)
countByThree() //13
countByThree() //16
countByTwo() //6 //checa como no se sobreescribió el valor de la función
countByThree() // 19

Ponte bien trucha: Fijate bien cabrón, la función makeCounter esta devolviendo otra función, de seguro ya viste que los valores que devuelven countByTwo y countByThree son independientes, porque la variable startAt tiene contextos distintos, no es compartida por las instancias de las funciones, porque sí recuerdas que las funciones son instancias de clases, ¿Verdad wey?. Cada que llamas a la función makeCounter se crea una instancia independiente.

Closures

Los closures son una variedad de funciones, y a diferencia de los blocks de objc, tienen una sintaxis mas clara, flexible y poderosa, en otras palabras, está más vergas:

var cuadradoClosureFullSyntax = {
  (x :Int) -> Int in
  return x*x
}
cuadrado(4)

Esta es la sintaxis completa, pero estos weyes de apple piensan en todo, entonces vas a encontrar algunas funciones que te pueden recibir un closure, por ejemplo el método para ordenar un array, volvamos a nuestro array de edades, lo vamos a poner de chiquito a grandote como en la primaria:

edades.sort( { (left :Int, right :Int) -> Bool in
  return left < right //menor a mayor
})

OK, ¿Qué chingaos pasó aquí?. Lo que pasó es que el método sort de las instancias de array toma un parámetro que es un closure, que  a su vez toma dos parámetros que los tienes que comparar y decir cual va primero devolviendo true. Pero eso no es todo!… No nos olvidemos de la inferencia de datos, como estas escribiendo el closure como parámetro de una función, entonces el compilador puede saber cual es el tipo de datos esperado y puedes simplificar la sintaxis:

edades.sort { left, right -> Bool in
  return left > right //mayor a menor
}

No, eso no es un typoo, como el closure es el único parámetro, entonces puedes omitir los paréntesis. Y aún hay más!… si llama en los siguientes 5 minutos, le reducimos aún más su pinchi closure:

edades.sort { left, right -> Bool in
 left > right //mayor a menor
}

Como el compilador sabe que el closure tiene que devolver un Bool, y solo hay una sentencia en el closure, te puedes ahorrar el return. Pero espere!!!, si es de los primeros 5 en llamar, le ahorraremos también la declaración de los parámetros y el tipo de retorno del closure:

edades.sort { $0 < $1 } //menor a mayor // I know, Mind fucking blown...
edades

Ejercicio. Las trais!!, ordena el array de nombres

Clases, structs y enums

OK, a partir de aquí puedes borrar el playground, te garantizo que ya no lo usarás. Todos estos p2 no son nada nuevo, son conceptos comunes, con sus detalles especiales en swift.

De entrada, los tres pueden contener funciones, variables y constantes, así como clases, structs o enums anidados, con lo que puedes organizar tus clases por namespaces.

Clases

Las clases siempre se pasan por referencia, pueden contener métodos(funciones), constantes y variables en las instancias y en las clases,

class Animal {
  var patasCount :Int
  var ojosCount :Int
  var especie :String
  init (patasCount :Int, ojosCount: Int, especie: String) {
    self.patasCount = patasCount
    self.ojosCount = ojosCount
    self.especie = especie
  }
  func descripcion() -> String {
    return "Soy un \(self.especie) con \(patasCount) patas y \(ojosCount) ojos"
  }
}
let kissyfur = Animal (patasCount: 2, ojosCount: 2, especie: "Oso")
kissyfur.descripcion()

Existen métodos especiales para inicializar las instancias, se indican con el nombre init, que son como los constructores, puedes usar la palabra self para distinguir las variables locales a la función init de las variables de la instancia. Si no le asignas un valor inicial a las variables debes inicializarlas en el init.

Existe una contraparte para el init que se ejecuta cuando la instancia se va a eliminar de memoria, se llama deinit, tanto init como deinit serán llamados en el momento adecuado, no lo haces de manera manual.

Agrega un método deinit donde tu animalito se despida de la vida.

En swift solo existe la herencia sencilla, no herencia múltiple, aunque se puede extender una clase de otras formas como los protocols y las class extension. Por ahora confórmate con esto:

class Perro: Animal {
  let nombre :String
  init(nombre :String, edad: Int) {
    self.nombre = nombre
    super.init(patasCount: 4, ojosCount: 2, especie: "Perro", edad: edad)
  }
  func ladra() -> String {
    return "waw"
  }
  override func descripcion() -> String {
    return super.descripcion() + "; soy \(self.nombre)"
  }
}

let pluto = Perro(nombre: "Pluto", edad: 4)
pluto.descripcion()
pluto.ladra();
pluto.ladra()

Como puedes ver, no es necesario inicializar las constantes de instancia en el momento de compilación, pero solo se pueden asignar una vez (en la inicialización)

Puedes sobreescribir una función heredada si antepones la palabra override antes de la declaración de la función, aunque esto no es necesario para los inicializadores; puedes acceder a la función primitiva haciendo referencia a la superclase con la palabra super.

Structs

A diferencia de objc, los structs en swift pueden tener funciones, otros structs o enums anidados, una diferencia con las clases, es que los structs siempre se pasan como copia.

¿Cuando usar structs y cuando clases?, bueno tienes que considerar que si es un objeto que estarás pasando de aquí pa’allá constantemente, en el caso de structs cada que lo asignas o lo pasas a una función, se creará una copia, por lo que los cambios que realices no persistirán al retorno de la función.

Los structs comparten muchas características con las clases, como los métodos e inicializadores.

Intenta lo siguiente en un nuevo Playground o borra el que ya tienes:

import UIKit // usaremos funciones matemáticas

struct Coordenada {
  var x :Double = 0
  var y :Double = 0
  init (x :Double, y :Double) {
    self.x = x
    self.y = y
  }
  func toPolar() -> (r :Double, α :Double) {
    return (sqrt(x*x+y*y), atan2(y,x))
  }
}

let coordenada = Coordenada(x: 1,y: sqrt(3))
let coordenadasPolares = coordenada.toPolar()
coordenadasPolares.α //radianes
coordenadasPolares.r

var coordenada2 = coordenada
coordenada2.x += 1

coordenada2
coordenada

Checa que las instancias de coordenada y coordenada2 son distintas e independientes, cada que pases un struct una nueva instancia se crea. Otra nota, checa que para devolver alfa en la tuple usé el carácter α, ya que swift te permite usar unicode en los nombres de variables y de funciones mientras empiecen con un carácter no numérico.

Enums

Al igual que los structs, los enums pueden conten funciones o enums anidados y siempre se pasan por copia. Los enums pueden tener un valor “raw” equivalente (un valor primitivo) pero no es obligatorio como en C. Lo indicas con dos puntos seguido del tipo Raw, como en la primera linea:

enum Coin: Int {
  case Tails = 0
  case Heads = 1
  init () {
    if rand()%2 == 0 { // requiere: import UIKit
      self = Coin.Tails
    } else {
      self = Coin.Heads
    }
  }
  func name() -> String {
    switch self {
    case .Tails:
      return "Sello"
    case .Heads:
      return "Cara"
    }
  }
}

enum Resultado {
  case Etsito (result :String, yourBet: Coin)
  case YouFail (result :String)
}

func flipACoin(yourBet:Coin) -> Resultado {
  let coin = Coin()
  if (yourBet == coin) {
    return Resultado.Etsito(result: "Ganas, te salió " + coin.name(), yourBet: yourBet)
  }
  return Resultado.YouFail(result: "Perdiste, te salió " + coin.name())
}

var ganados = 0

for intento in 1...100 {
  let intento1 = flipACoin(Coin.Heads)
  switch intento1 {
  case let .Etsito(resultado, miApuesta):
    println(resultado)
    ganados++
  case let .YouFail(resultado):
    println(resultado)
  }
}

ganados

Una característica bien cabrona es que a demás del valor raw, puedes asociar un valor arbitrario al enum, y con esto te ahorras crear otras estructuras auxiliares para darle sentido al enum:

    return Resultado.Etsito(result: "Ganas, te salió " + coin.name(), yourBet: yourBet)

En el ejemplo le puedes adjuntar un mensaje en caso de éxito o fracaso,  estos mensajes los puedes desempaquetar en un switch como se ve en el ejemplo:

case let .Etsito(resultado, miApuesta):

El case crea 2 constantes donde se reciben la tuple.

That’s all folks

OK, puede que hayan sido mas de 7 min, pero con estos alumnos se hace lo posible… No me chingues, no soy mago.

Esto es solo un curso intensivo, pero puedes checar las páginas individuales de cada característica que agregaré poco a poco para mayor detalle.

Cheers everybody!!

Aprende a programar para iPhone con Swift: Intro

De seguro ya lo conoces, o lo has oído, Swift es el nuevo lenguaje de programación de Apple,  anunciado en el Keynote de junio de 2014; aunque Objective-C seguirá siendo más usado y más soportado en corto, Swift trae muchas cosas bien chingonas que le harán la vida más fácil a los huevones como tu comprenderás.

La neta es que aunque Objective-C le había hecho la lucha para actualizarse, el pedo que tenía era que a huevo tenía que considerar limitaciones por el soporte a código escrito en C, no es que eso sea malo, pero digo, en cierta manera lo limitaba.

Cuando salieron los primeros fragmentos de código, los primeros que se emocionaron en la oficina fueron los weyes de Javascript, porque de entrada… sí le da un aire. Javascript puede traer cosas interesantes, es un lenguaje muy flexible, pero me da gusto decir que Swift, no es Javascript, pero sí agarra algo de varios lenguajes.

Si haz usado objetive-c, se te harán conocidos los blocks, protocols, el estilo de la herencia, el manejo de memoria con ARC o los parámetros con nombre; pero también jala conceptos de otros lenguajes como: seguridad de tipo de dato, sobrecarga de operadores, templates en clases y  funciones, niveles de control de acceso a miembros, inferencia de datos, y otros…

Creo que Apple pudo hacer un lenguaje muy expresivo y que va a atraer a muchos developers que se sentían fuera de lugar programando con objective-c, que por cierto, puedes mezclar código de obj-c y swift, aunque en archivos diferentes.

Otro punto bien chingon es que con el xCode 6 puedes tirar código y ver el resultado de lo que estas haciendo prácticamente en tiempo real, se llaman playground, y creeme, eso esta, bien cabron, puedes ver un demo en el Link del keynote que puse al inicio de este post, o también en el futuro pondré un tutorial de como usarlo.

Para terminar, te voy a enseñar un programa completo en swift mostrando el tradicional hola mundo:

println("Hola mundo")

That’s it, Peace out!

Si te interesó el artículo, puedes aprender a programar en swift en 7 minutos.