Home » Swift » Swift 3, análisis de sus cambios (I)
Swift 3 (I)

Swift 3, análisis de sus cambios (I)

Comenzamos un nuevo especial por entregas, que cubrirá todo el verano hasta el lanzamiento de las nuevas versiones major de Xcode y Swift. Un análisis que ahonda en todos y cada uno de los cambios aprobados para el proyecto Swift Open Source y que han sido incorporados a Swift 3. Un análisis donde vamos a ejemplificar cada caso y explicarlo con detenimiento.

SE-0004 Eliminación de operadores ++ y

Si venimos de C o cualquiera de sus variantes, sabremos que poner a++ aumenta el valor de a en 1. Pero este operador causaba confusión porque dependiendo de donde pusiéramos el ++ o el --, la operación hacía una cosa u otra.

En estos casos expuestos, el valor de a2 será 2 (el valor de a original) pero luego el valor de a será 3. Esto sucede porque al asignar, primero copiaba el valor original en a2 y luego aplicaba el operador ++, aumentando el valor en 1 de la propia variable a sin afectar a la asignación hecha en a2. Pero en el caso de a3, primero se hacía el incremento en 1 de a y una vez realizado, se copiaba el valor ya incrementado a a3. Es decir, el incremento se hace antes o después de asignar el valor de la variable a otra, en función de donde ++ estuviera colocado.

Esto provocaba bastante confusión en los desarrolladores, por lo que Apple ha aceptado la solución más simple: eliminar estos operadores. De esta forma, si queremos incrementar tendremos que usar a += 1 como equivalente a a = a + 1 y nada más.

Esto supone que ya no se podrán realizar incrementos ni decrementos en una asignación, como habíamos hecho en el ejemplo para Swift 2.

SE-0003 Borrado del uso de var de los parámetros en funciones

Swift 2 tiene una máxima en cuanto a los parámetros de las funciones: todo parámetro enviado en modo standard es de solo lectura. Esto quiere decir que cuando hacemos una cabecera func test(par1:Int) el parámetro par1 es un let: una constante. Lo que hace el lenguaje es realizar una copia del valor que enviamos desde la llamada y dejarlo inmutable. Por lo tanto si queremos trabajar con el valor enviado, hay que convertirlo a una variable que use otro nombre.

A efectos de funcionamiento de lenguaje, lo que hacía Swift 2 era incluir de manera explícita (y oculta) un let en la llamada, de forma que de cara al compilador la cabecera de la función era en realidad func test(let par1:Int).

Pero ahora se ha quitado esta opción: primero eliminando este let (lo que corresponde a la propuesta SE-0053) y luego liberando el uso del nombre de la variable. No significa que podamos usar ahora libremente el parámetro como si fuera var, si no que si queremos podemos crear una variable dentro de la propia función que se llame igual.

En resumen, lo que ahora pasa es que ya no podemos usar var antecediendo al nombre del parámetro en la especificación (la cabecera) y se ha quitado para usarlo directamente en la implementación con la instrucción que vemos var par1 = par1. Al estar dentro del ámbito de la función y ser una copia, al finalizar esta se libera. Solo si usamos el parámetro inout como ya hacíamos modificaremos el valor de fuera de la función, como hasta ahora.

SE-0031 Posición del modificador inout en la especificación

Esta es simple. En Swift 2, el parámetro inout que nos permite pasar un valor que podrá ser modificado dentro de la función y que también será modificado al terminar esta, fuera de su propio ámbito, cambia. Antes se ubicaba a la izquierda del nombre del parámetro y ahora se ubicará a la izquierda del tipo del parámetro.

Este programa funcionará igual en Swift 3, pero el inout deberá ir modificando al tipo Int y no al parámetro par1 como en el ejemplo anterior.

SE-0022 Referencia a un selector Objective-C de un método

El selector es una herramienta de gran valor en Objective-C, y una de esas funciones que usamos bastante en UIKit. Unos de los ejemplos más claros es el uso de acciones para un botón. Y este cambio, aunque se aprobó para Swift 3, ya se implantó desde Swift 2.2 así que intentaremos explicarlo de una forma clara, con el pequeño cambio introducido en Swift 3.

En Swift 2.2 tenemos dos opciones. La primera de ellas es usar los selectores directos a una función en Swift a través de la palabra clave Selector y el nombre de la función o método como cadena. Si usamos los dos puntos es porque la función tendrá un parámetro de entrada que será el objeto que haya ejecutado el envío (el sender). No obstante, tendremos una advertencia que nos dirá que accion no es un selector válido de Objective-C. Y si lo hacemos visible usando la directiva @objc antecediendo al nombre de la función entonces nos dirá que cambiemos la estructura del selector a una donde se hace una referencia directa a la especificación.

En Swift 3, tenemos también dos posibles casos: el primero es donde queramos seguir usando el selector como cadena pero la función no sea marcada con la directiva @objc. En ese caso, se nos insta a meter el selector entre dobles paréntesis para silenciar el warning (la advertencia) y con ello trabajar sin problema con el código.

Si cambiamos y ponemos la directiva @objc, el código seguirá siendo válido con el doble paréntesis y no se quejará. No obstante, la forma ideal es usar la nueva directiva #selector ya introducida en Swift 2.2, pero en este caso tenemos un pequeño cambio.

Mientras en Swift 2.2 hacíamos referencia a #selector(Formulario.accion(_:)) donde llamábamos a la propia clase por su nombre y especificábamos la llamada de parámetros como (_:), ahora en Swift 3 hay que hacer la llamada con todos sus parámetros.

Esta implementación requerirá siempre el uso de @objc antecediendo al nombre de la función a la que referenciemos, con el objeto de exponerla al otro lenguaje. Obviamente, esto no será problema si usamos algún método como destino del selector que ya pertenezca a Cocoa. Por ejemplo, el caso de let sel = #selector(UIView.insertSubview(_:atIndex:)).

SE-0032 Añadir el método first(where:) al tipo Sequence

Y cerramos este primer especial con un nuevo método. Cuando normalmente usamos la función indexOf en un dato de tipo Sequence (por ejemplo, un array), lo que se nos devuelve es un dato de tipo Index? (es decir, opcional), de forma que si buscamos si un valor existe o no, la función devolverá la primera posición donde ha sido encontrado o nil en caso de no encontrarlo.

Ahora, podemos usar predicados como closures y que se nos devuelva el primer valor encontrado en la secuencia, pero no su posición si no simplemente el valor en sí. Solo tenemos que usar el método first(where:) de las secuencias.

Este método hará una búsqueda, igual que la que haríamos nosotros recorriendo con un bucle for in hasta encontrar el resultado, salirnos de la iteración y devolver el resultado. Es una forma más simple de evitar el código:

Es una forma más rápida y cómoda de hacer lo mismo, con un mejor control sobre los errores y de forma genérica, por lo que no tendremos que adaptarnos a cada tipo de dato. Podemos usarlo como parte de un array de cualquier tipo y usar el predicado (el closure que devuelva un valor Bool como resultado) para indicar qué buscamos.

Conclusiones

Como podemos ver, los cambios son muy interesantes, entre mejoras, añadidos o quitar determinadas cosas que no aportan o pueden crear confusión. Os invitamos a probarlo con Swift Playgrounds para iOS 10 o en Xcode 8 y darnos vuestra opinión al respecto. Un saludo 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 Hoja de Ruta

Swift 3.1, hoja de ruta hasta primavera de 2017

Swift 3.1 marca su hoja de ruta de aquí a primavera de 2017 con dos grandes retos: la compatibilidad de código con la versión 3.0 y potenciar el gestor de paquetes y la integración con servidor para conseguir aun más rendimiento en soluciones como Kitura, Vapor o Perfect (entre otros). Descubre los detalles en nuestra noticia y qué nos depara esta nueva versión menor del lenguaje.