Una guía para desarrolladores de iOS: de Objective-C a Learning Swift
Publicado: 2022-03-11En 2008, Apple anunció y lanzó el iPhone SDK 2.0. Este evento inició otra revolución en el desarrollo de software y nació una nueva generación de desarrolladores. Ahora son reconocidos como desarrolladores de iOS.
Muchos de estos desarrolladores nunca antes habían usado Objective-C, y ese fue el primer desafío que Apple les lanzó. A pesar de la sintaxis desconocida y la gestión manual de la memoria, tuvo un gran éxito y ayudó a poblar la App Store con decenas de miles de aplicaciones. Apple mejoró continuamente Objective-C con cada lanzamiento, agregando bloques y literales, administración de memoria simplificada con conteo automático de referencias y muchas otras características indicativas de un lenguaje de programación moderno.
Y después de 6 años mejorando y trabajando en Objective-C, Apple decidió lanzar otro desafío a los desarrolladores. Una vez más, los desarrolladores de iOS deberán aprender un nuevo lenguaje de programación: Swift . Swift elimina la administración de punteros inseguros e introduce nuevas funciones potentes, al tiempo que mantiene la interacción con Objective-C y C.
Swift 1.0 ya es una plataforma de desarrollo sólida y estable, que seguramente evolucionará de manera interesante en los próximos años. Es un momento perfecto para comenzar a explorar este nuevo lenguaje, ya que obviamente es el futuro del desarrollo de iOS.
El propósito de este tutorial es brindarles a los desarrolladores de Objective-C una descripción general rápida de las nuevas funciones del lenguaje Swift, ayudándolos a dar el siguiente paso y comenzar a adoptar Swift en su trabajo diario. No pasaré mucho tiempo explicando Objective-C y supondré que está familiarizado con el desarrollo de iOS.
Probar Swift vs. Objective-C
Para comenzar a explorar Swift, todo lo que necesita hacer es descargar XCode de la App Store y crear un área de juegos para experimentar. Todos los ejemplos mencionados en este artículo se realizan de esta manera.
La página de inicio de Swift de Apple es la mejor referencia para aprender a programar Swift. Encontrará que es invaluable, y hasta que esté completamente al día con el desarrollo de Swift, creo que volverá aquí a menudo.
Variables y constantes
La declaración de una variable en Swift se realiza mediante la palabra clave var
.
var x = 1 var s = "Hello"
Notarás que dos variables x
y s
son de diferentes tipos. x
es un número entero, mientras que s
es una cadena. Swift es un lenguaje de tipo seguro y deducirá los tipos de variables del valor asignado. Si desea que su código sea más legible, opcionalmente puede anotar el tipo de variable:
var y: Int y = 2
Las constantes son similares, pero las declara usando let
en lugar de var
. No es necesario conocer el valor de una constante en el momento de la compilación, pero debe asignarle un valor exactamente una vez.
let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time
Como sugiere su nombre, son inmutables, por lo que el siguiente código provocará un error en tiempo de compilación.
let c = 1 c = 3 // error
Otros tipos también se pueden declarar como constantes. Por ejemplo, el siguiente código declara una matriz como una constante, y si intenta modificar alguno de los elementos, el compilador Swift informará un error:
var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error
Opcionales
Las constantes deben inicializarse al declararlas, y las variables deben inicializarse antes de su uso. Entonces, ¿dónde está el equivalente nil
de Objective-C? Swift introduce valores opcionales . Los valores opcionales pueden tener un valor o ser nil
. Si observa el siguiente código, notará que a x
se le asignó un valor Optional
de 2014
. Esto significa que el compilador de Swift sabía que x
también podría ser nil
.
var s = "2014" var x = s.toInt() print(x) // Optional(2014)
Si realiza un cambio en este código y asigna el valor "abc"
a s
, que no se puede convertir a un número entero, notará que x
ahora es un nil
.
var s = "abc" var x = s.toInt() print(x) // nil
El tipo de retorno de la función toInt()
es Int?
, que es un Int opcional . Intentemos llamar a una función estándar en x
:
var x = "2014".toInt() print(x.successor()) // error
El compilador señala un error, ya que x
es opcional y potencialmente podría ser nil . Primero tenemos que probar x
y asegurarnos de que la función successor
se invoque en un número real, y no en un valor nil
:
var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }
Tenga en cuenta que tenemos que desenvolver x
agregando un signo de exclamación (!) . Cuando estamos seguros de que x
contiene un valor, podemos acceder a él. De lo contrario obtendremos un error de tiempo de ejecución. También podemos hacer lo que Swift llama enlace opcional , convirtiendo lo opcional en una variable no opcional.
let x = "123".toInt() if let y = x { print(y) }
El código en la instrucción if
solo se ejecutará si x
tiene un valor y se lo asigna a y
. Tenga en cuenta que no necesitamos desenvolver y
, su tipo no es opcional ya que sabemos que x
no es nil
.
Consulte el tutorial de Swift de Apple para leer más detalles sobre opciones y características agradables como el encadenamiento opcional
Interpolación de cadenas
En Objective-C, el formato de cadenas generalmente se realiza con el método stringWithFormat:
::
NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];
Swift tiene una función llamada interpolación de cadenas para hacer lo mismo, pero es más compacta y fácil de leer:
let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"
También puedes usar expresiones:
let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"
Para obtener más información sobre la interpolación de cadenas de Swift y otras características nuevas, vaya aquí.
Funciones
La definición de función en Swift es diferente de C. A continuación se muestra una definición de función de muestra:
func someFunction(s:String, i: Int) -> Bool { ... // code }
Las funciones Swift son tipos de primera clase . Esto significa que puede asignar funciones a variables, pasarlas como parámetros a otras funciones o hacer que devuelvan 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
Nuevamente, Swift infiere los tipos de f1
y f2
( String
-> Int
), aunque podríamos haberlos definido explícitamente:
let f1:String -> Int = stringLength
Las funciones también pueden devolver 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))
Puede encontrar una guía de funciones en Swift aquí.
enumeraciones
Las enumeraciones en Swift son mucho más poderosas que en Objective-C. Como estructuras Swift, pueden tener métodos y se pasan por valor:
enum MobileDevice : String { case iPhone = "iPhone", Andro, 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 de Swift pueden asignar cadenas, caracteres o flotantes como valores para cada miembro, además de números enteros. El práctico método toRaw()
devuelve 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)"
Más información sobre las enumeraciones está disponible aquí.

tuplas
Las tuplas agrupan múltiples valores en un solo valor compuesto. Los valores dentro de una tupla pueden ser de cualquier tipo y no tienen que ser del mismo tipo entre sí.
let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel
También puede nombrar los elementos de tupla individuales:
let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)
Las tuplas son extremadamente convenientes como tipos de devolución para funciones que necesitan devolver 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 admite la coincidencia de patrones en una declaración de cambio:
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 parte real del número, por lo que usamos un _
para hacer coincidir cualquier cosa. También puede consultar condiciones adicionales en cada caso. Para eso, necesitamos vincular los valores del patrón a las constantes:
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)") }
Tenga en cuenta cómo necesitamos vincular solo los valores que vamos a usar en la comparación o en la declaración del caso.
Para leer más sobre las tuplas, vaya aquí.
Clases y Estructuras
A diferencia de Objective-C, Swift no requiere que cree archivos de interfaz e implementación separados para clases y estructuras personalizadas. A medida que aprende Swift, aprenderá a definir una clase o una estructura en un solo archivo, y la interfaz externa a esa clase o estructura estará automáticamente disponible para que la use otro código.
Definición de clases
Las definiciones de clase son muy simples:
class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())
Como puede ver, la declaración y la implementación están en el mismo archivo . Swift ya no usa archivos de encabezado e 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á porque la etiqueta es una variable no opcional y no tendrá un valor cuando se crea una instancia de Bottle. 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 bien, podríamos usar el tipo Optional
para una propiedad, que no debe inicializarse. En el siguiente ejemplo, convertimos el volume
en un Optional Integer
:
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 una 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)" } }
Al igual que las clases en Swift, las estructuras pueden tener métodos, propiedades, inicializadores y cumplir con los protocolos. La principal diferencia entre clases y estructuras es que las clases se pasan por referencia, mientras que las estructuras se pasan por valor .
Este ejemplo demuestra el paso de 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 un caso similar con struct
, notará que las variables se pasan por valor:
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 debemos usar class
? Al igual que en Objective-C y C, use estructuras cuando necesite agrupar algunos valores y espere que se copien en lugar de hacer referencia a ellos. Por ejemplo, números complejos, puntos 2D o 3D o colores RGB.
Una instancia de una clase se conoce tradicionalmente como un objeto. Sin embargo, las clases y estructuras de Swift tienen una funcionalidad mucho más cercana que en otros lenguajes, y mucha funcionalidad se puede aplicar a instancias de una clase o un tipo de estructura. Debido a esto, el término más general que se usa en la referencia de Swift es instance
, que se aplica a cualquiera de estos dos.
Aprenda los conceptos básicos de las clases y estructuras de Swift aquí.
Propiedades
Como vimos anteriormente, las propiedades en Swift se declaran con la palabra clave var
dentro de una definición de clase o estructura. También podemos declarar constantes con una 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 tenga en cuenta que las propiedades de clase están fuertemente referenciadas, a menos que les prefije con la palabra clave weak
. Sin embargo, hay algunas sutilezas con propiedades no opcionales débiles, así que lea el capítulo de conteo automático de referencias en la guía Swift de Apple.
Propiedades calculadas
Las propiedades calculadas en realidad no almacenan un valor. En su lugar, proporcionan un getter y un setter opcional para recuperar y establecer otras propiedades y valores indirectamente.
El siguiente código proporciona una muestra de un sign
de valor calculado:
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 simplemente implementando un getter:
struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }
En Objective-C, las propiedades suelen estar respaldadas por una variable de instancia, declarada explícitamente o creada automáticamente por el compilador. En Swift, por otro lado, una propiedad no tiene una variable de instancia correspondiente . Es decir, no se puede acceder directamente al almacén de respaldo de una propiedad. Supongamos 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
Dado que, en Swift, las propiedades calculadas no tienen un almacén de respaldo, debemos hacer 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 nuevas más importantes para aprender en Swift, como los genéricos, la interacción con las bibliotecas de Objective-C, los cierres, el encadenamiento opcional y la sobrecarga de operadores. Un solo tutorial no puede describir a fondo un nuevo lenguaje, pero no tengo dudas de que se escribirá mucho más sobre el aprendizaje de la programación Swift. Sin embargo, creo que esta lectura rápida ayudará a muchos desarrolladores de Objective-C, que no han logrado encontrar tiempo y aprender los detalles del lenguaje Swift, ponerse en marcha y dejar que el pájaro Swift los lleve a nuevas alturas.