Swift desde Cero: Opcionales y Flujo de Control
() translation by (you can also view the original English article)
En los artículos anteriores, aprendiste algunos de los conceptos básicos del lenguaje de programación Swift. Si has programado antes, estoy seguro de que viste algunas similitudes con otros lenguajes de programación, como Ruby, JavaScript, y Objective-C.
En este artículo, nos adentramos al flujo de control en Swift. Antes de que podamos discutir el flujo de control en más detalle, necesitamos echar un vistazo a lo que es más nuevo para ti, opcionales. Los opcionales son otra característica de seguridad en Swift. En principio, podría parecer una molestia usar opcionales, pero aprenderás rápidamente que los opcionales harán tu código mucho más seguro.
1. Opcionales
Ya hemos visto que una variable debe ser inicializada antes de poder ser usada. Echa un vistazo al siguiente ejemplo para entender mejor lo que esto significa.
1 |
var str: String |
2 |
|
3 |
str.isEmpty |
Si estás acostumbrado a trabajar con cadena en Objective-C, entonces podrías sorprenderte de que Swift te muestre un error. Veamos lo que el error nos dice.



En muchos lenguajes, las variables tienen un valor inicial por defecto. En Objective-C, por ejemplo, la cadena en el siguiente código es igual a nil
.
1 |
NSString *newString; |
Sin embargo, el concepto de nil
difiere en Swift y Objective-C. Discutiremos nil
a más detalle un poco después.
¿Qué es un opcional?
Swift usa opcionales para encapsular un concepto importante, que es, una variable o constante tiene un valor o no lo tiene. Es así de simple en Swift. Para declarar una variable o constante como opcional, agregamos un signo de interrogación al tipo de variable o constante.
1 |
var str: String? |
La variable str
ya no es del tipo String
. Es ahora del tipo opcional String
. Esto es importante de entender. El resultado o efecto secundario es que ya no podemos interactuar directamente con el valor de la variable str
. El valor es almacenado de manera segura en el opcional y necesitamos pedir al opcional el valor que encapsula.
Desenvolvimiento Forzado
Una manera de acceder al valor de un opcional es a través de desenvolvimiento forzado. Podemos acceder al valor de la variable str
agregando un !
al nombre de la variable.
1 |
var str: String? |
2 |
|
3 |
str = "Test" |
4 |
|
5 |
println(str!) |
Es importante que se asegures de que el opcional contiene un valor cuando lo desenvuelves de manera forzada. Si el opcional no tiene un valor y lo desenvuelves de manera forzada, Swift te arrojará un error.



Enlace Opcional
Hay una manera más segura de acceder al valor de un opcional. Echaremos un vistazo más de cerca a las declaraciones if
en unos cuantos minutos, pero el siguiente ejemplo muestra como podemos acceder de manera segura al valor almacenado en la variable str
, el cuál es un tipo de String
opcional.
1 |
var str: String? |
2 |
|
3 |
if str != nil { |
4 |
println(str!) |
5 |
} else { |
6 |
println("str has no value") |
7 |
}
|
Primero revisamos si la variable str
es igual a nil
antes de imprimir sus contenidos. En este ejemplo, str
no tiene un valor, lo que significa que no será desenvuelto a la fuerza por accidente.
Hay una aproximación más elegante llamada enlazado opcional. En el siguiente ejemplo, asignamos el valor almacenado en el opcional a una constante temporal, que es usada en la declaración if
. El valor del opcional str
está atado a la constante strConst
y usado en la declaración if
. Esta aproximación también funciona para declaraciones while
.
1 |
var str: String? |
2 |
|
3 |
str = "Test" |
4 |
|
5 |
if let strConst = str { |
6 |
println(strConst) |
7 |
} else { |
8 |
println("str has no value") |
9 |
}
|
¿Qué es nil
?
Si vienes de Objective-C, entonces muy seguramente sabes que es nil
. En Objective-C, nil
es un apuntador a un objeto que no existe. Swift define nil
un poco diferente y es importante que entiendas la diferencia.
En Swift, nil
significa la ausencia de un valor, cualquier valor. Mientras que nil
es solo aplicable a objetos en Objective-C, en Swift nil
puede ser usado para cualquier tipo. Es por lo tanto importante entender que un opcional no es el equivalente de nil
en Objective-C. Estos conceptos son muy diferentes.
2. Flujo de Control
Swift ofrece un número de constructores comunes para controlar el flujo del código que escribes. Si tienes cualquier experiencia programando, entonces no tendrás problemas poniéndote al corriente con los constructores de flujo de control de Swift, declaraciones condicionales if
y switch
, y ciclos for
y while
.
Sin embargo, Swift no sería Swift si su flujo de control no fuera diferente de, por ejemplo, los constructores de flujo de control de Objective-C. Mientras que los detalles son importantes, estoy seguro de que no te impedirán ponerte al corriente con Swift. Comencemos con el constructor condicional más común, la declaración if
.
if
Las declaraciones if
de Swift son muy similares a aquellas encontradas en Objective-C. La principal diferencia es que no hay necesidad de envolver la condición en paréntesis. Las llaves, sin embargo, son obligatorias. Estos últimos previenen a los desarrolladores de introducir bugs comunes que están relacionados con escribir declaraciones if
sin llaves. Así es como se ve una declaración if
en Swift.
1 |
let a = 10 |
2 |
|
3 |
if a > 10 { |
4 |
println("The value of \"a\" is greater than 10.") |
5 |
} else { |
6 |
println("The value of \"a\" is less than or equal to 10.") |
7 |
}
|
No debería ser una sorpresa que Swift también defina una cláusula else
. El código en la cláusula else
es ejecutado si la condición es igual a false
. También es posible encadenar declaraciones if
como se muestra en el siguiente ejemplo.
1 |
let a = 10 |
2 |
|
3 |
if a > 10 { |
4 |
println("The value of \"a\" is greater than 10.") |
5 |
} else if a > 5 { |
6 |
println("The value of \"a\" is greater than 5.") |
7 |
} else { |
8 |
println("The value of \"a\" is less than or equal to 5.") |
9 |
}
|
Hay una nota importante que hacer, eso es, la condición de una declaración if
necesita devolver true
o false
. Esto no es verdadero para declaraciones if
en Objective-C. Echa un vistazo a la siguiente declaración if
en Objective-C.
1 |
NSArray *array = @[]; |
2 |
|
3 |
if (array.count) { |
4 |
NSLog(@"The array contains one or more items."); |
5 |
} else { |
6 |
NSLog(@"The array is empty."); |
7 |
}
|
Si fuéramos a portar el pedazo de código de arriba a Swift, nos encontraríamos con un error. El error no es muy informativo, pero Swift nos dice que necesitamos asegurar que el resultado de la condición evalúa a true
o false
.



La manera correcta de traducir el código Objective-C de arriba a Swift es asegurar que la condición de la declaración if
evalúa a true
o false
, como en el siguiente código.
1 |
let array = [String]() |
2 |
|
3 |
if array.count > 0 { |
4 |
println("The array contains one or more items.") |
5 |
} else { |
6 |
println("The array is empty.") |
7 |
}
|
switch
La declaración switch
de Swift es más poderosa que su equivalente de Objective-C. También es más segura como aprenderás en un momento. Mientras que hay algunas diferencias, las declaraciones switch
en Swift adhieren al mismo concepto a aquellas en otros lenguajes de programación, un valor es pasado a la declaración switch
y es comparada contra posibles patrones coincidentes.
Así es, patrones. Como dije, una declaración switch
en Swift tiene unos cuantos trucos bajo la manga. Echaremos un vistazo a esos trucos en un momento. Hablemos de seguridad primero.
Exhaustivo
Una declaración switch
en Swift necesita ser exhaustiva, significando que cada posible valor del tipo que es pasado a la declaración switch
necesita ser manejado por la declaración switch
. Como en Objective-C, esto es fácilmente resuelto agregando un caso default
como en el siguiente ejemplo.
1 |
let a = 10 |
2 |
|
3 |
switch a { |
4 |
case 0: |
5 |
println("a is equal to 0") |
6 |
case 1: |
7 |
println("a is equal to 1") |
8 |
default: |
9 |
println("a has another value") |
10 |
}
|
Fracaso
Una diferencia importante con la implementación de declaraciones switch
con Objective-C es la falta de fracaso implícito. El siguiente ejemplo no funciona en Swift por unas cuantas razones.
1 |
let a = 10 |
2 |
|
3 |
switch a { |
4 |
case 0: |
5 |
case 1: |
6 |
println("a is equal to 1") |
7 |
default: |
8 |
println("a has another value") |
9 |
}
|
El primer caso en el cuál a
es comparado contra 0
no cae implícitamente al segundo caso en el cuál a
es comparado contra 1
. Si agregas el siguiente ejemplo a tu playground, notarás que Swift te arroja un error. El error dice que cada caso necesita incluir al menos una declaración ejecutable.
Nota que los casos de la declaración switch
no incluyen declaraciones break
fuera de la declaración switch
. Esto no es requerido en Swift ya que el fracaso implícito no existe en Swift. Esto eliminará un rango de bugs comunes causados por fracasos no intencionales.
Patrones
El poder de una declaración switch
en Swift yace en el emparejamiento de patrón. Echa un vistazo al siguiente ejemplo en el cuál he usado rangos para comparar los valores considerados.
1 |
let a = 10 |
2 |
|
3 |
switch a { |
4 |
case 0..<5: |
5 |
println("The value of a lies between 0 and 4.") |
6 |
case 5...10: |
7 |
println("The value of a lies between 5 and 10.") |
8 |
default: |
9 |
println("The value of a is greater than 10.") |
10 |
}
|
El operador ..<
del operador de rango medio-abierto define un rango para el primer valor al segundo valor, excluyendo el segundo valor. El operador ...
o operador de rango cerrado define un rango desde el primer valor al segundo valor, incluyendo el segundo valor. Estos operadores son muy útiles en un amplio rango de situaciones.
También puedes comparar el valor considerado de una declaración switch
a tuplas. Echa un vistazo al siguiente ejemplo para ver como funciona esto.
1 |
let latlng = (34.15, -78.03) |
2 |
|
3 |
switch latlng { |
4 |
case (0, 0): |
5 |
println("We're at the center of the planet.") |
6 |
case (0...90, _): |
7 |
println("We're in the Northern hemisphere.") |
8 |
case (-90...0, _): |
9 |
println("We're in the Southern hemisphere.") |
10 |
default: |
11 |
println("The coordinate is invalid.") |
12 |
}
|
Como puedes ver en el ejemplo de arriba, es posible que el valor coincida con más de un caso. Cuando esto sucede, el primer caso coincidente es elegido. El ejemplo de arriba también ilustra el uso del guión bajo. Como vimos en el artículo anterior, podemos usar un guión bajo, _
, para decirle a Swift en cuáles valores no estamos interesados.
Enlace de Valor
El enlace de valor también es posible con declaraciones switch
como demuestra el siguiente ejemplo. El segundo valor de la tupla está temporalmente unido a la constante description
para uso en el primer y segundo caso.
1 |
var response = (200, "OK") |
2 |
|
3 |
switch response { |
4 |
case (200..<400, let description): |
5 |
println("The request was successful with description \(description).") |
6 |
case (400..<500, let description): |
7 |
println("The request was unsuccessful with description \(description).") |
8 |
default: |
9 |
println("The request was unsuccessful with no description.") |
10 |
}
|
for
El ciclo for
es el primer constructor de ciclo que veremos. Se comporta muy similar a los ciclos for
en otros lenguajes. Hay dos sabores, el ciclo for
y el ciclo for-in
.
for
El ciclo for
de Swift es casi idéntico a un ciclo for
en Objective-C como ilustra el siguiente ejemplo. El ciclo for
ejecuta un número de declaraciones hasta que una condición predefinida se cumple.
1 |
for var i = 0; i < 10; i++ { |
2 |
println("i is equal to \(i).") |
3 |
}
|
Como con las declaraciones if
, no hay necesidad de usar paréntesis para encerrar la inicialización del ciclo, condición y definiciones de incremento. Las declaraciones de ciclo, sin embargo, necesitan estar encerradas por llaves.
for-in
El ciclo for-in
es ideal para ciclar sobre los contenidos de un rango o colección. En el siguiente ejemplo, ciclamos sobre los elementos de un arreglo.
1 |
let numbers = [1, 2, 3, 5, 8] |
2 |
|
3 |
for number in numbers { |
4 |
println("number is equal to \(number)") |
5 |
}
|
También podemos usar ciclos for-in
para ciclar sobre los pares llave-valor de un diccionario. En el siguiente ejemplo, declaramos un diccionario e imprimimos sus contenidos a la consola. Como vimos antes en la serie, la secuencia de pares llave-valor es indefinida ya que un diccionario es un conjunto desordenado de pares llave-valor.
1 |
var bids = ["Tom": 100, "Bart": 150, "Susan": 120] |
2 |
|
3 |
for (name, bid) in bids { |
4 |
println("\(name)'s bid is $\(bid).") |
5 |
}
|
Cada par llave-valor del diccionario está disponible en el ciclo for-in
como una tupla de constantes nombradas. El ciclo for-in
también es grandioso en combinación con rangos. Estoy seguro de que estás de acuerdo en que el código de arriba es fácil de leer y entender gracias al uso de un rango cerrado.
1 |
for i in 1...10 { |
2 |
println("i is equal to \(i)") |
3 |
}
|
while
El ciclo while
también viene en dos variantes, while
y do-while
. La principal diferencia es que el conjunto de declaraciones de un ciclo do-while
siempre se ejecuta al menos una vez, porque la condición del do-while
es evaluada al final de cada iteración. El siguiente ejemplo ilustra esta diferencia.
1 |
var c = 5 |
2 |
var d = 5 |
3 |
|
4 |
while c < d { |
5 |
println("c is smaller than d") |
6 |
}
|
7 |
|
8 |
do { |
9 |
println("c is smaller than d") |
10 |
} while c < d |
La declaración println
del ciclo while
nunca es ejecutada mientras que la del ciclo do-while
se ejecuta una vez.
En muchos casos, los ciclos for
pueden ser reescritos como ciclos while
y frecuentemente depende del desarrollador determinar qué tipo de ciclo usar en una situación en particular. Los siguientes ciclos for
y while
resultan en la misma salida.
1 |
for var i = 0; i < 10; i++ { |
2 |
println(i) |
3 |
}
|
4 |
|
5 |
var i = 0 |
6 |
|
7 |
while i < 10 { |
8 |
println(i) |
9 |
i++ |
10 |
}
|
Aprende Más en Nuestro Curso de Programación Swift
Si estás interesado en llevar tu educación Swift al siguiente nivel, puedes echar un vistazo a nuestro curso completo sobre desarrollo Swift.
Conclusión
Hay mucho más sobre flujo de control en Swift que hemos cubierto en este artículo, pero ahora tienes un entendimiento básico para continuar tu viaje a Swift. Espero que este tutorial te haya mostrado que la implementación de flujo de control de Swift es muy similar a otros lenguajes de programación, con un giro.
En el resto de la serie, haremos más uso de constructores de flujo de control y tendrás un mejor entendimiento gradualmente de las sutiles diferencias con lenguajes como Objective-C. En la siguiente entrada de esta serie, comenzaremos explorando funciones.