Guía para Desarrolladores iOS: Desde Objective-C hasta Swift Parte2



BY MARCO MUSTAPIC - IOS DEVELOPER @ TOPTAL

Funciones

La definición de Función en Swift es diferente a la de C. Una muestra de definición de función es la siguiente:
func someFunction(s:String, i: Int) -> Bool
{
...    // code    
}
Las funciones Swift son tipos de primera clase. Esto quiere decir que puedes asignar funciones a las variables, hacerlas pasar como parámetros para las funciones o hacerlas regresar tipos:
func stringLength(s:String) -> Int
{
    return countElements(s)
}

func stringValue(s:String) -> Int
{
    if let x = s.toInt()
    {
        return x
    }
    return 0
}

func doSomething(f:String -> Int, s:String) -> Int
{
    return f(s).successor()
}

let f1 = stringLength
let f2 = stringValue

doSomething(f1, "123")    // 4
doSomething(f2, "123")    // 124
De nuevo, Swift infiere los tipos de f1 and f2 (String -> Int), Aunque los pudimos haber definido explícitamente:
let f1:String -> Int = stringLength
Las funciones también pueden regresar otras funciones:
func compareGreaterThan(a: Int, b: Int) -> Bool
{
    return a > b
}

func compareLessThan(a: Int, b: Int) -> Bool
{
    return a < b
}

func comparator(greaterThan:Bool) -> (Int, Int) -> Bool
{
    if greaterThan
    {
        return compareGreaterThan
    }
    else
    {
        return compareLessThan
    }
}

let f = comparator(true)
println(f(5, 9))
Una guía para las funciones en Swift se encuentra aquí.

Enumeraciones

Las enumeraciones en Swift son mucho más ponderosas que en Objective-C. Como lo estructura Swift, pueden tener métodos y se pasan como valores:
enum MobileDevice : String
{
    case iPhone = "iPhone", Android = "Android", WP8 = "Windows Phone8", BB = "BlackBerry"

    func name() -> String
    {
        return self.toRaw()
    }
}
let m = MobileDevice.Android
print(m.name())    // "Android"
A diferencia de Objective-C, las enumeraciones Swift pueden asignar cadenas, caracteres o flotantes como valores para cada miembro, aparte de números enteros. El método conveniente toRaw() regresa el valor asignado a cada miembro.
Las enumeraciones también se pueden parametrizar:
enum Location
{
    case Address(street:String, city:String)
    case LatLon(lat:Float, lon:Float)
    
    func description() -> String
    {
        switch self
        {
        case let .Address(street, city):
            return street + ", " + city
        case let .LatLon(lat, lon):
            return "(\(lat), \(lon))"
        }
    }
}

let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco")
let loc2 = Location.LatLon(lat: 23.117, lon: 45.899)
print(loc1.description())        // "2070 Fell St, San Francisco"
print(loc2.description())        // "(23.117, 45.988)"
Puedes encontrar más información sobre las enumeraciones aquí.

Tuplas

Las tuplas agrupan valores múltiples en un único valor compuesto. Los valores dentro de una tupla pueden ser de cualquier tipo y no tiene que ser del mismo tipo entre ellos.
let person = ("Gabriel", "Kirkpatrick")
print(person.0) // Gabriel
También puedes darle nombre a los elementos de tuplas individuales:
let person = (first: "Gabriel", last: "Kirkpatrick")
print(person.first)
Las tuplas son extremadamente convenientes como tipos de regreso para funciones que necesitan regresar más de un valor:
func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int)
{
    return (a/b, a%b)
}
print(intDivision(11, 3))    // (3, 2)
let result = intDivision(15, 4)
print(result.remainder)    // 3
A diferencia de Objective-C, Swift apoya la búsqueda de patrones en case statement o switch case:
let complex = (2.0, 1.1)    // real and imaginary parts

switch complex
{
    case (0, 0):
        println("Number is zero")
    case (_, 0):
        println("Number is real")
    default:
        println("Number is imaginary")
}
En el segundo caso, no nos importa la verdadera parte del número así que usamos un _ para que pueda coincidir con cualquier cosa. También puedes comprobar si hay condiciones adicionales en cada caso. Para esto, es necesario unir los valores de patrón:
let complex = (2.0, 1.1)

switch complex
{
    case (0, 0):
        println("Number is zero")
    case (let a, 0) where a > 0:
        println("Number is real and positive")
    case (let a, 0) where a < 0:
        println("Number is real and negative")
    case (0, let b) where b != 0:
        println("Number has only imaginary part")
    case let (a, b):
        println("Number is imaginary with distance \(a*a + b*b)")
}
Nota como necesitamos unir solo los valores que vamos a usar en la comparación o en el switch case.
Puedes leer más sobre Tuplas aquí.

Clases y Estructuras

A diferencia de Objective-C, Swift no requiere que crees documentos de interfaz e implementación por separado para clases y estructuras personalizadas. Mientras aprendes sobre Swift, aprenderás a definir una clase o estructura en un solo documento y la interfaz externa para esa clase o estructura se hace disponible automáticamente para el uso de otro código.

Definir Clases

Las definiciones de clase son my sencillas:
class Bottle
{
   var volume: Int = 1000
   
   func description() -> String
   {
       return "This bottle has \(volume) ml"
   }
}
let b = Bottle()
print(b.description())
Como puedes ver, declaración e implementación están en el mismo documento. Swift ya no utiliza un encabezado ni documentos de implementación. Agreguemos una etiqueta a nuestro ejemplo:
class Bottle
{
   var volume: Int = 1000
   var label:String
   
   func description() -> String
   {
       return "This bottle of \(label) has \(volume) ml"
   }
}
El compilador se quejará, ya que la etiqueta es una variable no-opcional, y ésta no mantendrá un valor cuando se ejemplifica una Bottle (Botella). Necesitamos agregar un inicializador:
class Bottle
{
   var volume: Int = 1000
   var label:String
   
   init(label:String)
   {
       self.label = label
   }

   func description() -> String
   {
       return "This bottle of \(label) has \(volume) ml"
   }
}
O podríamos usar tipo Opcional para una propiedad, el cual no necesita ser inicializado. En el siguiente ejemplo convertimos en volumen un Número entero Opcional:
class Bottle
{
   var volume: Int?
   var label:String
   
   init(label:String)
   {
       self.label = label
   }

   func description() -> String
   {
        if self.volume != nil
        {   
               return "This bottle of \(label) has \(volume!) ml"
           }
           else
           {
               return "A bootle of \(label)"
           }
   }
}

Estructuras

El lenguaje Swift también tiene structs, pero son mucho más flexibles que en Objective-C. El siguiente tutorial de código define un struct:
struct Seat
{
    var row: Int
    var letter:String
    
    init (row: Int, letter:String)
    {
        self.row = row
        self.letter = letter
    }
    
    func description() -> String
    {
        return "\(row)-\(letter)"
    }
}
Como las clases en Swift, las estructuras pueden tener métodos, propiedades, inicializadores y se ajustan a los protocolos. La diferencia principal entre clases y estructuras es que las clases se pasan por referencia, mientras que las estructuras lo hacen por valor.
Este ejemplo demuestra el pasar las clases por referencia:
let b = Bottle()
print(b.description())    // "b" bottle has 1000 ml

var b2 = b
b.volume = 750
print(b2.description())    // "b" and "b2" bottles have 750 ml
Si intentamos hacer algo similar con struct, notarás que las variables se pasan con valores:
var s1 = Seat(row: 14, letter:"A")
var s2 = s1
s1.letter = "B"
print(s1.description())    // 14-B
print(s2.description())    // 14-A
¿Cuándo debemos usar struct y cuándo usamos class? Al igual que en Objective-C y C, usa structs cuando necesites agrupar algunos valores y espera que sean copiados en vez de referenciados o colores RGB.
La instancia de una clase es conocida tradicionalmente como un objeto. Sin embargo, las clases y estructuras Swift son mucho más cercanas en funcionalidad que en otros lenguajes y se puede aplicar mucha funcionalidad a instancias de tipo estructura o clase. Por esto, el término más general utilizado en referencia Swift es instancia, el cual se aplica a cualquiera de estos dos.
Aprende lo básico sobre las clases y estructuras Swift aquí.

Propiedades

Como vimos anteriormente, las propiedades en Swift se declaran con la palabra clave var dentro de la definición de una clase o estructura. También podemos declarar con la instrucción let.
struct FixedPointNumber
{
    var digits: Int
    let decimals: Int
}

var n = FixedPointNumber(digits: 12345, decimals: 2)
n.digits = 4567    // ok
n.decimals = 3     // error, decimals is a constant
También ten en cuenta que las propiedades de la clase son fuertemente referenciadas, a menos que uses el prefijo weak como palabra clave. Sin embargo, hay algunas sutilezas con propiedades no-opcionales de weak, así que lee el capítulo Contabilidad de referencia automática en la Guía Swift de Apple..

Propiedades Calculadas

Las propiedades calculadas no almacenan un valor. Por el contrario, proporcionan un getter y un setter opcional para recuperar y establecer otras propiedades y valores indirectamente.
El siguiente código proporciona un ejemplo de un valor calculado sign:
enum Sign
{
    case Positive
    case Negative
}

struct SomeNumber
{
    var number:Int
    var sign:Sign
    {
        get
        {
            if number < 0
            {
                return Sign.Negative
            }
            else
            {
                return Sign.Positive
            }
        }
        
        set (newSign)
        {
            if (newSign == Sign.Negative)
            {
                self.number = -abs(self.number)
            }
            else
            {
                self.number = abs(self.number)
            }
        }
    }
}
También podemos definir propiedades de solo lectura con solo implementar un getter:
struct SomeNumber
{
    var number:Int
    var isEven:Bool
    {
        get
        {
            return number % 2 == 0
        }
    }
}
En Objective-C, las propiedades usualmente se respaldan con una variable de instancia, declarada explícitamente o creada automáticamente por el compilador. Por otra parte, en Swift, una propiedad no tiene una variable de instancia correspondiente. Es decir, no se puede acceder directamente al almacén de respaldo de una propiedad. Supón que tenemos esto en Objective-C:
// .h
@interface OnlyInitialString : NSObject

@property(strong) NSString *string;

@end

// .m

@implementation OnlyInitialString

- (void)setString:(NSString *newString)
{
    if (newString.length > 0)
    {
        _string = [newString substringToIndex:1];
    }
    else
    {
        _string = @"";
    }
}

@end
Ya que en Swift las propiedades calculadas no tienen un almacén de respaldo, necesitamos algo como esto:
class OnlyInitialString
{
    var initial:String = ""
    var string:String
    {
        set (newString)
        {
            if countElements(newString) > 0
            {
                self.initial = newString.substringToIndex(advance(newString.startIndex, 1))
            }
            else
            {
                self.initial = ""
            }
        }
        get
        {
            return self.initial
        }
    }
}
Las propiedades se explican con más detalle aquí

Continuará

Hay muchas cosas más importantes y nuevas que aprender en Swift como programación genérica, interacción con las bibliotecas Objective-C, cierres, optional chaining y sobrecarga de operadores. Un solo tutorial no puede describir completamente un nuevo lenguaje, pero no tengo dudas de que se escribirá mucho más sobre programación Swift. Sin embargo, creo que esta lectura rápida ayudará a muchos desarrolladores Objective-C, quienes no han encontrado el tiempo, ni detalles de aprendizaje sobre el lenguaje Swift, ponte en marcha y deja que el pájaro Swift te lleve a nuevas alturas.

Visita la Parte 1
Artículo vía Toptal

Déjame tus dudas y comentarios: Conversion Conversion Emoticon Emoticon

Entradas Populares