
Swift 5.0 estuvo disponible con el lanzamiento de Xcode 10.2, pero el trabajo en la próxima versión continúa y ya hay noticias de lo que puede esperar en él.
Una característica clave de
Swift 5.1 es la
estabilidad del módulo , lo que nos permite usar bibliotecas de terceros sin preocuparnos de con qué versión del compilador Swift fueron creadas. Parece la
estabilidad ABI que obtuvimos en Swift 5.0, pero hay una ligera diferencia: la estabilidad ABI resuelve las diferencias en las versiones de Swift en tiempo de ejecución y la estabilidad del módulo en tiempo de compilación.
Además de esta importante innovación, recibiremos varias mejoras importantes en Swift, y en este artículo las repasaremos con ejemplos para que pueda verlas en acción.
Ser universal
SE-0068 expande el uso de
Self , de modo que se refiere al tipo que lo contiene dentro de clases, estructuras y enumeraciones. Esto suele ser útil para los tipos dinámicos cuando es necesario determinar el tipo exacto de algo en tiempo de ejecución.
Como ejemplo, considere el siguiente código:
class NetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(NetworkManager.maximumActiveRequests).") } }
Aquí definimos la propiedad estática
maximumActiveRequests dentro de la clase
NetworkManager y agregamos el método
printDebugData () para imprimir esta propiedad. Aquí todo está bien, pero solo hasta que decidamos heredar de
NetworkManager :
class ThrottledNetworkManager: NetworkManager { override class var maximumActiveRequests: Int { return 1 } }
En este heredero, cambiamos la propiedad maximumActiveRequests para que ahora sea igual a uno, pero si llamamos a
printDebugData () ,
inferirá el valor de la clase padre:
let manager = ThrottledNetworkManager() manager.printDebugData()
Aquí deberíamos obtener 1 en lugar de 4, y aquí viene el rescate SE-0068: podemos usar
Self (con la 'S' mayúscula) para referirnos al tipo actual. Así que ahora podemos reescribir el método
printDebugData () de la clase padre de esta manera:
class ImprovedNetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(Self.maximumActiveRequests).") } }
Es decir,
Self funciona de la misma manera que funcionaba en protocolos en versiones anteriores de Swift.
Advertencias de ambigüedad ninguna
Las opciones en Swift se implementan como una enumeración con dos opciones:
algunas y
ninguna . Esto puede generar confusión si creamos nuestra propia enumeración que no tiene una opción y la envolvemos en
opcional . Por ejemplo:
enum BorderStyle { case none case solid(thickness: Int) }
Cuando se utiliza no opcional, todo está limpio:
let border1: BorderStyle = .none print(border1)
Esto generará "ninguno". Pero si usamos opcional para esta enumeración, entonces encontraremos un problema:
let border2: BorderStyle? = .none print(border2)
Aquí se imprimirá cero, ya que Swift cree que .none significa que opcional está
vacío , aunque en realidad es opcional
con un valor de BorderStyle.none.
En Swift 5.1, en caso de tal ambigüedad, se mostrará una advertencia:
"Suponiendo que te refieres a 'Opcional.Ninguno'; ¿quiso decir 'BorderStyle.none' en su lugar?
Por lo tanto, se informará al desarrollador que con su código no todo puede ser sencillo.
Emparejamiento de enumeraciones opcionales y no opcionales
Swift es lo suficientemente inteligente como para comprender la construcción de cambio / caso al combinar texto opcional / no opcional y valores enteros, pero no en el caso de enumeraciones.
Ahora en Swift 5.1 podemos usar switch / case para hacer coincidir las opciones de enumeración opcionales y no opcionales:
enum BuildStatus { case starting case inProgress case complete } let status: BuildStatus? = .inProgress switch status { case .inProgress: print("Build is starting…") case .complete: print("Build is complete!") default: print("Some other build status") }
Swift puede asignar enumeraciones opcionales a opciones no opcionales, y aquí se mostrará "La construcción está comenzando ...".
Comparar colecciones ordenadas
SE-0240 introdujo la capacidad de calcular diferencias entre colecciones ordenadas, así como aplicar el resultado de comparación resultante a colecciones. Esto puede ser de interés para los desarrolladores que tienen colecciones complejas en una vista de tabla y necesitan agregar o eliminar muchos elementos usando animación.
El principio básico es simple: Swift 5.1 proporciona una nueva
diferencia de método
(de :) , que determina las diferencias entre dos colecciones ordenadas: qué elementos agregar y cuáles eliminar. Esto se aplica a cualquier colección ordenada que contenga artículos que cumplan con el protocolo
Equatable .
Para demostrar esto, crearemos dos matrices de valores, calcularemos las diferencias de una y otra, y luego revisaremos la lista de diferencias y las aplicaremos para que las dos colecciones sean iguales.
Nota: dado que Swift ahora se distribuye como parte de los sistemas operativos de Apple, se deben utilizar nuevas herramientas de idioma con la verificación
#disponible para asegurarse de que el código se ejecute en un sistema operativo que admita la funcionalidad requerida. Para la funcionalidad que se ejecuta en sistemas operativos desconocidos y no anunciados que pueden lanzarse en el futuro, se utiliza un número de versión especial, "9999", que significa: "Todavía no conocemos el número de versión correcto".
var scores1 = [100, 91, 95, 98, 100] let scores2 = [100, 98, 95, 91, 100] if #available(iOS 9999, *) { let diff = scores2.difference(from: scores1) for change in diff { switch change { case .remove(let offset, _, _): scores1.remove(at: offset) case .insert(let offset, let element, _): scores1.insert(element, at: offset) } } print(scores1) }
Para una animación más avanzada, podemos usar el tercer parámetro en la lista resultante de diferencias:
relatedWith . Por lo tanto, en lugar de
.insert (let offset, let element, _), podemos escribir .insert (let offset, let element, letsociatedWith). Esto nos da la capacidad de rastrear simultáneamente pares de cambios: mover el elemento en la colección dos posiciones hacia abajo es eliminar el elemento y luego agregarlo, y
asociado con "une" estos dos cambios juntos y le permite considerar este movimiento.
En lugar de aplicar las diferencias manualmente, una por una, puede aplicarlas de una sola vez utilizando el nuevo método de
aplicación () :
if #available(iOS 9999, *) { let diff = scores2.difference(from: scores1) let result = scores1.applying(diff) ?? [] }
Crear matrices sin inicializar
SE-0245 introdujo un nuevo inicializador para matrices que no lo llena con valores predeterminados. Estaba disponible anteriormente como una API privada, lo que significaba que Xcode no lo solicitó al completar el código, pero puede usarlo si lo necesita y comprende el riesgo de que esta funcionalidad no sea en el futuro.
Para usar el inicializador, establezca el tamaño de la matriz, luego pase el cierre que llena la matriz con valores. Un cierre recibirá un puntero inseguro a un búfer mutable, así como un segundo parámetro en el que indica cuántos valores realmente utiliza.
Por ejemplo, podemos crear una matriz de 10 enteros aleatorios como este:
let randomNumbers = Array<Int>(_unsafeUninitializedCapacity: 10) { buffer, initializedCount in for x in 0..<10 { buffer[x] = Int.random(in: 0...10) } initializedCount = 10 }
Hay varias reglas:
- No necesita usar todo el volumen que solicitó, pero no puede excederlo. Es decir, si establece el tamaño de la matriz en 10, puede establecer initializedCount en el rango de 0 a 10, pero no 11.
- si no inicializó los elementos utilizados en la matriz, por ejemplo, configuró initializedCount en 5, pero no proporcionó valores reales a los elementos 0 a 4, lo más probable es que reciban valores aleatorios. Como sabes, esta es una mala opción.
- Si no configura initializedCount , será igual a 0 y se perderán todos los datos que asignó.
Sí, podríamos reescribir el código usando
map () :
let randomNumbers2 = (0...9).map { _ in Int.random(in: 0...10) }
Obviamente, esto es más legible, pero no tan efectivo: creamos un rango, luego una nueva matriz vacía, le asignamos un tamaño y "revisamos" todo el rango, aplicando un cierre a cada elemento.
Conclusión
Swift 5.1 todavía está en desarrollo, y aunque la rama final para Swift ya pasó, los cambios de algunos otros proyectos relacionados aún son visibles.
Entonces, el cambio más importante es la
estabilidad del módulo , y se sabe que el equipo de desarrollo está trabajando duro en esto. No dieron una fecha de lanzamiento exacta, aunque dijeron que Swift 5.1 tuvo un tiempo de desarrollo significativamente más corto en comparación con Swift 5.0, que requirió una concentración extraordinaria de energía y atención. Podemos asumir el acceso a WWDC19, pero es obvio que este no es el caso cuando necesita apresurarse a una fecha determinada.
Otro punto que merece atención. Dos cambios en esta lista (“Advertencias en caso de ambigüedad de la opción ninguno” y “Emparejamiento de enumeraciones opcionales y no opcionales”) no fueron el resultado de la evolución de Swift, pero se reconocieron como errores y se ajustaron.