Introducción
Como hemos comentado previamente, una de las ventajas que nos da Swift es la posibilidad de escribir código seguro. Esto implica que swift está preparado para que las aplicaciones no se encuentren con situaciones indeseadas al momento de ejecutar el código. Los opcionales son un mecanismo muy importante para conseguir este objetivo.
Los opcionales permiten indicar que un valor puede estar ausente. Esto significa que una variable opcional puede tener un valor y por lo tanto se puede acceder al mismo o que esa variable esta “vacía”.
La potencia de este enfoque es que si se sabe que una variable puede llegar a no tener valor en algún momento de su ciclo de vida, podemos escribir código adicional preguntando si ese valor existe o no, estando seguros en todo momento sobre qué hacer en cada caso.
Si no existieran los opcionales, puede darse el caso de que no contemplemos un escenario en donde el valor no exista y por lo tanto se generen comportamientos extraños en momentos de ejecución. Veamos un ejemplo:
let colores = ["azul", "negro", "verde", "rojo", "amarillo"] let primerColor = colores.first //primerColor es es String? en lugar de String
Primero definimos un array de String
donde guardamos un listado de colores y luego, creamos una constante para obtener el primero color del arreglo usando la propiedad first
.
Como puede darse el caso de que el array esté vacío, first
no siempre va a devolver un valor; en algunos casos tomará el primer elemento pero en otros indicará que no se encontró un valor. Esto hace que el resultado sea un opcional.
nil
Volviendo al ejemplo anterior, para indicar la ausencia de un valor se usa un valor especial llamado nil
.
Por lo tanto, la constante primerColor no se trata de un tipo String
sino de un opcional de String
, indicado con un signo de pregunta (?) String?
.
Si quisiéramos definir una variable como opcional usamos la siguiente forma:
var segundoColor: String? = "negro" print("\(segundoColor)") //Devuelve: //Optional(“negro”) segundoColor = nil print("\(segundoColor)") //Devuelve: //nil
Como vemos, definimos la variable indicando el tipo de dato y agregando un signo de pregunta para indicar que se trata de un opcional.
La asignación de un valor en ese momento es optativo, ya que de no hacerlo Swift infiere que su valor inicial es nil
.
Si imprimimos esa variable usando print
vemos que su valor es Optional(“negro”) en lugar de “negro” a secas.
Una variable opcional puede volver a valer nil
si es necesario.
Recordemos que las variables no opcionales deben tener un valor al momento de su definición obligatoriamente.
Forced unwrapping
Si sabemos y estamos seguros que en un determinado momento una variable opcional tiene un valor en su interior, podemos usar la técnica de forced unwrapping (desenvoltura forzada) mediante el uso de un signo de exclamación al final del nombre de la variable (!):
var otroColor:String? = "celeste" print(“\(otroColor!)”) //Devuelve //celeste
Como sabemos que la variable otroColor contiene un valor, usamos la desenvoltura forzada y accedemos al dato que el mismo encierra. Ya no estamos obteniendo un Optional(“celeste”) sino que directamente obtenemos “celeste”.
Si quisiéramos preguntar primero si esa variable vale nil
o no, una posibilidad es usar una sentencia if
:
if otroColor != nil { print("\(otroColor!)") } //Devuelve //celeste
En este caso, primero nos aseguramos si la variable no vale nil
(o sea, si tiene un valor) y luego hacemos el forced unwrapping, debido a que dentro del if
estamos seguros que esa variable tiene un valor.
Optional Binding
Si bien el enfoque anterior es perfectamente válido, no termina siendo muy cómodo si dentro del if
se necesita usar muchas veces el valor dentro del opcional, ya que tendríamos que recordar de usar el !
en todos los casos.
Otra manera de hacer lo mismo pero un poco más eficiente es usando el optional binding (enlace opcional), en el cual se pregunta si un opcional contiene un valor y en caso afirmativo, se crea una constante o variable temporal para ser usada dentro del if
o while
como si se tratara de una versión no opcional. Una vez fuera de ese if
o while
, la misma deja de existir.
if let colorElegido = otroColor { print("El color elegido es \(colorElegido)") } //Devuelve: //El color elegido es celeste
Como vemos, creamos una constante temporal llamada colorElegido que contiene el valor del opcional otroColor. Dentro del if
usamos ese valor y ya fuera de él la constante ya no existe más.
Asimismo, podemos anidar varios enlaces opcionales e incluso usar otras comparaciones convencionales que devuelven true
o false
. Como resultado, sólo se ejecutarán las instrucciones dentro del if
cuando todos los opcionales tengan un valor y todas las condiciones devuelvan true
. Si alguno de estos casos no se da, el resultado final será false
y no se ingresará al if
.
let numeroPar = 30 if let colorElegido = otroColor, let numero = Int("45"), numeroPar < 50 { print("El color elegido es \(colorElegido) y el \(numeroPar) es menor a 50") print("\(numero)") } //Devuelve: //El color elegido es celeste y el 30 es menor a 50 //45
Implicit unwrapped optionals
En muchas situaciones contamos con variables opcionales que en un momento determinado del programa son inicializados con un valor y que luego no van a volver a valer nil
nunca más. Por tal motivo, es conveniente no tener que preguntar a cada instante si esa variable contiene un valor y hacer la desenvoltura correspondiente, ya que se asume que siempre se va a contener un valor.
Un ejemplo muy claro de este escenario se da cuando se programa para un sistema operativo como iOS, en donde los botones que vemos en pantalla al inicio de su ciclo de vida se crean en nil
pero luego mientras se inicializan determinados componentes, las variables que hacen referencia a esos botones contienen valor, hasta tanto el usuario cambia de pantalla y se pierden esas referencias. Como en un momento de su ciclo de vida necesitó valer nil
, es obligatorio usar un opcional, pero como luego siempre va a tener un valor, no es cómodo tener que usar un if let
en cada uso. Para este tipo de casos, los implicity unwrapped optionals (opcionales desenvueltos en forma implícita) son ideales.
Para hacer uso de ellos, en lugar de declarar al opcional usando un signo de pregunta (?) se utiliza un signo de exclamación (!):
let posibleValor:String? = "Estamos aprendiendo Swift! Yeahh!" let valorSeguro:String! = posibleValor! print(posibleValor) print(valorSeguro) //Devuelve: //Optional("Estamos aprendiendo Swift! Yeahh!") //Estamos aprendiendo Swift! Yeahh!
Podríamos decir que este tipo de opcionales tienen realizan la desenvoltura de su valor en forma automática, sin necesidad de hacerlo explícito. Tengamos en cuenta que si una variable o constante vale nil
e intentamos usar su valor, vamos a tener un error en tiempos de ejecución. Por lo tanto, hay que ser muy cuidadoso y pensar bien antes de usar este tipo de opcionales.
En el caso de que exista alguna posibilidad de que una variable se vuelva nil
en algún momento del código, entonces no debemos usar este opcional implícito.
Por otro lado, si necesitamos preguntar si ese opcional implícito tiene un valor en su interior o si quisiéramos usar el optional binding, podemos hacerlo:
if valorSeguro != nil { print(valorSeguro) } if let dato = valorSeguro { print(dato) }
nil-coalescing operator
Este operador, que podríamos traducirlo como operador que viene junto a nil, devuelve el valor que contiene un opcional haciendo una desenvoltura (a) y en caso de encontrar nil
, devuelve otro valor (b). Su sintaxis es a ?? b
.
Un punto a tener en cuenta es que tanto a como b deben ser del mismo tipo.
var animal:String? = "Perro" let mascota = animal ?? "Gato" print("\(mascota)") //Devuelve Perro
En el ejemplo, definimos una variable opcional animal con el valor Perro. Al ser opcional, podría no contener un valor (ser nil
). Luego se declara una constante mascota que va a valer Perro siempre que la variable animal tenga ese valor. En caso de ser nil
, se incluye un valor adicional Gato.
Este operador es muy útil cuando queremos dar un valor por defecto en caso de que una variable o contante no tengan valor.