Home » Análisis » Swift 2, análisis de la evolución a través de las versiones beta
Swift 2 Evolution

Swift 2, análisis de la evolución a través de las versiones beta

Apple acaba de lanzar la versión 6 de la beta de Xcode 7 donde la principal novedad es la incorporación de nuevos añadidos a la especificación y sintaxis de Swift. Está claro que Swift 2 ha ido mejorando, en gran medida siguiendo las peticiones o sugerencias de los usuarios, y si el calendario se cumple el próximo 9 de septiembre de este 2015 veremos el lanzamiento de las versiones Golden Master de iOS 9 y Xcode 7, que anteceden al lanzamiento de las versiones finales que se prevén para el próximo 18 de septiembre.

Como complemento a este artículo, podéis repasar los principales cambios que incorpora Swift 2 en el artículo que hicimos en su lanzamiento o incluso podéis oír el episodio 4 de nuestro podcast que dedicamos íntegramente a analizar todos sus cambios y repercusión.

Repasemos versión por versión los más importantes cambios que se han incorporado y son actualmente funcionales en esta última versión Beta 6, no sin antes advertir que muchos de estos requieren conocimientos avanzados de Swift para ser comprendidos en toda su esencia, aunque vamos a intentar explicarlos lo más claro posible.

Beta 2

A las pocas semanas del lanzamiento, nos llegaron novedades en el lenguaje. La principal, la invocación de los inicializadores de tipos de manera explícita como funciones estáticas de los mismos. Podemos invocarlos tanto en el propio dato que creemos como en la referencia a su tipo estático:

Otra importante novedad es la posibilidad de poder aplicar métodos de structs, enumeraciones o protocolos a través de su propia invocación con self. En la práctica esto significa que cualquier método de una estructura que devuelva un resultado parcial de sí misma, ahora puede usarse como resultado de función dentro de otra.

Por ejemplo: con un conjunto o set, podemos usar la función union que nos devuelve como resultado el conjunto de valores comunes a dos conjuntos. Si tenemos un conjunto a y otro b y ejecutamos a.union(b) el resultado será un nuevo conjunto con todos sus elementos comunes. Pero, ¿y si queremos poder pasar dinámicamente a la función union un conjunto de resultados a través de, por ejemplo, una función map?.

En el ejemplo que Apple proporciona se ve muy claro: tenemos un conjunto a y un array con dos conjuntos. Ahora podemos hacer una función map del array de conjuntos y pasarle como parámetro al closure la función parcializada a.union que no tiene parámetro porque el propio map se lo proporcionará, obteniendo como resultado un array con dos posiciones que contienen la suma de elementos no comunes de cada uno de los conjuntos en cada una de las posiciones del array b.

Otra función interesante incorporada es la posibilidad de hacer cargas de datos en las enumeraciones a través de funciones, como map. Para ello, por ejemplo, imaginemos que tenemos una enumeración tipificada con dato asociado de una cadena y dentro, igualmente, sus posibles valores que pueden contener cadenas asociadas.

Si quisiéramos crear una carga de datos suele ser bastante tedioso, pero ahora basta crear un array del tipo de la enumeración, darle una serie de valores a través de un array y luego, usando la función map aplicamos el valor del case de la enumeración que queramos usar a todos esos casos.

Beta 3

Una de las novedades más interesantes que se incorporaron en esta versión es la inicialización implícita de valores en una enumeración con valor asociado. Hasta ahora, nosotros definíamos una enumeración de tipo cadena de la siguiente forma:

A la hora de definir una variable de cada case teníamos que asociarle un valor equivalente y a veces dicho valor era el mismo que queríamos darle, lo que nos obligaba a hacer algo así:

Ahora Swift hace el trabajo por nosotros y asocia el valor como cadena igual al case que corresponda. De esta forma, si ahora hacemos el Ejemplo A será exactamente igual que si hubiéramos hecho el Ejemplo B.

Uno de los cambios más importantes en Swift 2 es que toda variable que no vaya a ser usada debe ser sustituida por un _ como placeholder o hueco que indica que este valor es ignorado y no consume ni memoria ni recursos. De esta forma, si queremos crear un bucle de una secuencia pero no vamos a usar la variable indice que normalmente usaríamos, hemos de poner un _ en su lugar.

Esto es una característica propia de Swift 2 desde la primera versión, pero se ha ido extendido de forma que si ahora queremos usar un parámetro sin nombre para una función, como por ejemplo func f(Int) { } no podemos hacerlo y hemos de indicar este placeholder en su lugar. De esta forma la instrucción quedaría así: func f(_: Int) { }.

Otro cambio importante es el de los nombres de los parámetros genéricos dentro de la librería estándar de Swift. Si antes el parámetro genérico de un array era Array<T> ahora ha cambiado y especifica en su lugar que estamos hablando de un elemento de dicho array cambiando por Array<Element>.

Beta 4

En esta versión también hubo cambios muy importantes que prueban que la evolución de Swift va mucho más allá de pequeños retoques. El primero de ellos es la incorporación de la API completa de performSelector dentro de Swift. Esta serie de instrucciones, muy conocidas por aquellos que hayan trabajado en Objective-C, permiten ejecutar selectores asociados a funciones de manera dinámica y trabajar con ellos, incluso lanzarlos en hilos de ejecución que difieran del principal. Swift ya tiene su forma de trabajar con selectores, más simple, pero la incorporación de esta API facilita aun más la transición desde Objective-C y la traducción del código.

Selector: llamada a una función donde la invocación se realiza desde una cadena de texto lo que permite una invocación dinámica de llamadas en tiempo de programación.

Otra importante novedad es la introducción de las enumeraciones indirectas que permiten un uso recursivo de los mismas.

Recursividad: facultad de un objeto o método de llamarse a sí mismo para generar un comportamiento diferente al que originalmente tiene y creando un árbol de ejecución de varios niveles. Es un proceso muy común para, por ejemplo, trabajar con árboles de datos.

A partir del uso de la palabra clave indirect antecediendo a la declaración de la enumeración, podemos hacer que uno de los casos del nodo tenga como valor asociado la propia enumeración en sí, de forma que una enumeración puede contener una estructura igual a sí misma y de esta forma se genera un árbol infinito.

Esto nos permite crear nodos de un árbol cuyos nodos son una estructura igual a la que estamos usando, y generar un árbol binario de búsqueda bastante interesante y práctico en depende de qué casos a la hora de programar.

Otro cambio importante es que los tipos NSObject de Objective-C (integrado en Swift como objeto base) y el tipo AnyObject que engloba cualquier objeto en Swift, son ahora compatibles en modo de conversión el uno con el otro. De esta forma, podemos hacer que cualquier NSObject se convierta en AnyObject y cuando nos pueda interesar volver a recuperar la tipificación a NSObject. De igual forma, si usamos objetos de clases dentro de arrays, diccionarios o conjuntos (sets), ahora dichos tipos conectan correctamente con los equivalentes en Objective-C por lo que cualquier clase que nos devuelva ese tipo de dato ahora es tratada correctamente.

Beta 5

Siguen los cambios en un carrera sin fin de depuración del lenguaje. En esta versión Apple incorporó la posibilidad de conformar al protocolo ErrorType tanto clases como structs, con lo que permite una mejor gestión del control de errores de Swift 2.

Cuando en un inicializador propio o de conveniencia, usamos el inicializador designado y este es falible (puede devolver nil) podemos hacer un desempaquetado explícito de dicho resultado directamente usando !, lo que facilita el trabajo en casos donde sabemos que el inicializador designado no puede fallar.

Cuando trabajamos con Core Data, la base de datos de persistencia de Apple, ahora podemos usar el atributo @NSManaged no solo en las propiedades, si no también en los propios métodos que declaremos en la subclase de NSManagedObject.

Los diferentes casos de una enumeración o los nombres de tipos, ahora ya se conforman con el protocolo Printable de una manera correcta, convirtiéndose en cadena y mostrando sus valores sin mostrar códigos internos que no nos aportan dato alguno de interés.

Siguiendo con los cambios provocados por la orientación a protocolos que sigue todo Swift 2, ahora las secuencias de números a partir de un rango han incorporado una nueva función que permite iterar sobre ella: forEach. Veamos el siguiente ejemplo:

Ahora podemos hacer esto directamente desde un función embebida dentro de la propia secuencia, con la única diferencia que no podemos usar los modificadores de control de ejecución propios del for in como el return o el break.

Por último, aunque no menos importante, el método removeAtIndex en los diccionarios ahora devuelve una tupla con la clave y el dato, no como antes que solo devolvía el dato. De igual forma, este mismo método en los conjuntos ahora devuelve también el elemento borrado.

Beta 6

Y llegamos a la actual última versión de Xcode 7 en el momento que escribimos. En esta versión ha habido una gran e importante nueva incorporación: la nueva instrucción try?. El objetivo de la misma es intentar realizar una operación que podría provocar algún tipo de fallo, de forma que si la misma funciona el resultado de esta se guardará en un opcional y en caso que provoque algún error, devolverá nil. De esta forma, añadimos una capa más de seguridad al código dentro de una de las más importantes mejoras que Swift 2 tiene y a la vez mayores cambios en nuestra forma de trabajar: el control de errores.

De esta forma, si tenemos una función que puede fallar al ejecutarse y provocar un error fatal, normalmente la creamos añadiendo la palabra clave throws que implica esa posibilidad preparando al sistema para controlar el error.

Así podemos controlar el posible error y en caso que este se de, cadena sería nil y por lo tanto el enlace opcional fallaría y no entraría a ejecutar el código asociado al mismo. De igual forma podemos usar un guard.

De esta forma, con guard (en este enlace tenéis una explicación sobre este nuevo control de flujo) se convierte en un código muy útil porque si el proceso se ejecuta correctamente, seguirá hacía abajo ejecutando y capturando el valor opcional a través del enlace, y si no, entrará en el else del guard> y dará el error pertinente. O, por ejemplo, también podemos usar el operador de coalescencia nula ?? que nos permite evaluar una expresión opcional y en caso que esta falle, devolver un valor por defecto del mismo tipo que el devuelto por la función evaluada.

Este cambio se produce porque ahora en Swift 2 la mayoría de APIs de la librería estándar usan rethrows en los closures o en los parámetros de tipo @autoclosure. De esta forma, la evaluación de una función con throws permite controlar los posibles errores fatales de una función y se nos permite usarla dentro de cualquier closure. Podemos usar los métodos map o filter e incluso operadores comparativos y de coalescencia nula, como ya hemos visto.

Otro importante cambio es el que afecta a la instrucción print que ahora es capaz de hacer más cosas. La primera de ellas es que soporta valores variádicos, es decir, que podemos poner más de un valor seguido por comas y todos ellos serán impresos usando un separador por defecto que es el espacio en blanco.

El resultado que nos devolverá será Ola k ase 42 65.0 incluyendo un carácter \n de salto de línea al final. Pero es más práctico aun, porque podemos usar parámetros que nos permiten modificar su comportamiento, como por ejemplo el separador de todos esos elementos, de forma que si ponemos print(v1, v2, v3, separator:"@") conseguiremos que lo que se devuelva sea Ola k ase@42@65.0.

Y tenemos otro parámetro más: terminator que indica el carácter que servirá para finalizar la cadena y que por defecto es un salto de línea. Si no queremos este salto de línea solo tenemos que indicar print(v1, v2, v3, separator:"@", terminator:"").

Incluso podemos desviar la salida de la consola y capturarla en una variable. Basta con usar otro parámetro llamado toStream al que hay que pasarle el puntero de la cadena donde queremos almacenar el resultado mostrado por print.

En este caso, print no devolverá nada si no que todo el resultado será almacenado en cadenaSalida, como si fuera un habitual volcado de la salida del terminal con el comando ->.

Derivado de este cambio, ahora los parámetros variádicos pueden ir en cualquier lugar dentro de los parámetros de una función, no como antes que debían ir siempre como último parámetro. Por lo tanto podemos tener tantos como queramos y en el orden que queramos.

Otra pequeñas mejoras son, por ejemplo, el soporte de las propiedades estáticas calculadas en las extensiones de protocolos o el hecho que, por fin, cuando el tipo de una enumeración está clara para el compilador, se nos muestran opciones de completar los posibles valores del mismo al poner un . en todos los casos.

Por último, las funciones de división o corte de las colecciones, como prefix, suffix, dropFirst, dropLast y split han sido eliminadas y sustituidas por sus equivalentes como funciones dentro del protocolo CollectionType de forma que ya no se invocan como funciones si no como métodos embebidos en la colección. De esta forma, cumplen con el protocolo Sliceable.

Camino a Swift 2.0 final

Como hemos podido ver en este extenso artículo, son muchos los detalles que se han ido incorporando a Swift 2 en el transcurso de las betas hasta ahora. Si los cálculos no fallan, esta versión Beta 6 debería ser la última (penúltima en todo caso) antes de la versión Golden Master tanto de iOS 9 como de Xcode 7 antecediendo al lanzamiento oficial al público.

Nosotros mientras seguimos preparando la actualización gratuita de “Aprendiendo Swift” que actualizará y ampliará el libro para cubrir completamente Swift 2, y que tendréis disponible para todos los actuales compradores cuando Apple lance la versión final al público a finales del próximo mes. Hasta entonces, como siempre os decimos: probad, trabajad, aprended y Good Apple Coding.

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

Swift 3.1

Swift 3.1 ha llegado, análisis de todos sus cambios

Swift 3.1 ha llegado de la mano de la hornada de actualizaciones lanzadas por Apple. Analizamos sus cambios más importantes e incorporaciones más destacadas. Nuevas formas de convertir closures que no escapan en los que sí lo hace, conversiones seguras de números, genéricos más eficientes... descubre en nuestros análisis con ejemplos concretos todos los cambios y descúbrelos por ti mismo.