Swift 5.2 se liberó en marzo 2020; incluido Xcode V11.4. Esta versión del lenguaje ha traído una serie de mejoras y nuevas características que facilitan el desarrollo y mejoran la experiencia del programador. En esta versión se corrigen algunos errores y se afinan muchas características del lenguage Swift como la reducción del código y la memoria. Las principales áreas de mejoras son:
- Mejores diagnósticos con mensajes de error más útiles, especialmente para SwiftUI.
- Nuevas funcionalidades que simplificarán ciertas tareas.
- Corrección de errores importantes.
A continuación más detalles:
1. Mejoras en el sistema de diagnóstico
Uno de los cambios más significativos en Swift 5.2 es la mejora en los mensajes de error y advertencias. Se introdujo una nueva arquitectura de diagnóstico diseñada para optimizar la precisión y fiabilidad de los mensajes de error emitidos por Xcode en caso de errores de programación. Esta característica es especialmente valiosa en el contexto de SwiftUI, donde Swift solía generar un alto número de falsos positivos en los reportes de errores.
Ejemplo:Antes, un error como el siguiente podría ser poco informativo:
func greet(person: String) {
print("Hola, \(person)!")
}
greet(person: 5) // Error: type 'Int' cannot be used as a function argument type 'String'
Con la mejora de diagnóstico, el error ahora es más claro y específico sobre el tipo que se esperaba.
2. Nuevos protocolos Result y Callable
Swift 5.2 introdujo el protocolo Result, que permite manejar resultados y errores de manera más elegante. Sin embargo, el soporte de Callable aún está en fase de discusión.
Ejemplo de uso de Result:
enum NetworkError: Error {
case badURL
case requestFailed
}
func fetchData(from url: String, completion: @escaping (Result) -> Void) {
guard let validURL = URL(string: url) else {
completion(.failure(.badURL))
return
}
// Lógica de solicitud...
// En caso de éxito:
completion(.success(Data()))
}
fetchData(from: "https://example.com") { result in
switch result {
case .success(let data):
print("Datos recibidos: \(data)")
case .failure(let error):
print("Error: \(error)")
}
}
3. Múltiples KeyPaths y mejoras en @dynamicCallable
Swift 5.2 mejora el soporte para KeyPaths y introduce @dynamicCallable, facilitando la creación de tipos que pueden ser llamados como si fueran funciones.
Ejemplo de KeyPaths:
struct Person {
var name: String
var age: Int
}
let keyPath = \Person.name
let person = Person(name: "Juan", age: 30)
print(person[keyPath: keyPath]) // "Juan"
4. Mejoras en la sintaxis de @resultBuilder
Se mejoró la sintaxis y funcionalidad de los result builders, lo que permite construir tipos de forma más fluida y legible.
Ejemplo:
@resultBuilder
struct HTML {
static func buildBlock(_ components: String...) -> String {
return components.joined()
}
}
func createHTML(@HTML builder: () -> String) -> String {
return "\(builder())"
}
let htmlString = createHTML {
"Hello, World!"
}
print(htmlString) // "Hello, World!"
5.Expresiones de ruta de clave como funciones
Con la implementación de SE-0249, ahora podemos escribir código más legible y expresivo al acceder a propiedades de objetos. Esta propuesta nos permite usar una sintaxis más intuitiva, como “\.value“, en lugar de invocar métodos.
Por ejemplo:
struct User {
let name: String
let age: Int
var isAdolescent: Bool {
age >= 18
}
}
let juan = User(name: "Juan", age: 18)
let pere = User(name: "pereere", age: 19)
let orl = User(name: "Orl", age: 17)
let users = [juan, pere, orl]
//Antes tenias que hacer uso de un cierre para acceder a las propiedades:
let oldUserNames = users.map { $0.name }
//Ahora con la nueva sintaxis es mucho m´ás directo:
let userNames = users.map(\.name)
print(userNames)
//Este mismo enfoque funciona para otras funciones de primer nivel como Filter y CompactMap:
let voters = users.filter(\.isAdolescent)
let bestFriends = users.compactMap(\.age)
6. Valores invocables de tipos nominales definidos por el usuario
SE-0253 permite establecer valores estáticamente invocables en Swift, lo que quiere decir que ahora es posible invocar un valor directamente si su tipo implementa un método llamado callAsFunction()
. No necesitas cumplir con ningún protocolo especial ; solo necesitas agregar ese método a tu tipo.
struct Dice {
var lowerBound: Int
var upperBound: Int
func callAsFunction() -> Int {
(lowerBound...upperBound).randomElement()!
}
}
let d6 = Dice(lowerBound: 1, upperBound: 6)
let roll1 = d6()
print(roll1)
Puede llamar a callAsFunction() con parámetros. Puedes controlar el valor de retorno, marcarlos como mutating e incluso utilizar throws y rethrows. Puede también definir múltiples callAsFunction() en un solo tipo, Swift elegirá el correcto según el sitio de llamada, al igual que la sobrecarga normal.
7. Los subíndices de un tipo ahora pueden declarar parámetros predeterminados
Al agregar subíndices personalizados a un tipo, ahora puede usar argumentos predeterminados para cualquiera de los parámetros. Por ejemplo, si tuviéramos una estructura con un subíndice personalizado, podríamos agregar un parámetro default para enviar de vuelta si alguien intenta leer un índice fuera de los límites de la matriz:
//Ejemplo tomado de: https://www.hackingwithswift.com/articles/212/whats-new-in-swift-5-2
struct PoliceForce {
var officers: [String]
subscript(index: Int, default default: String = "Unknown") -> String {
if index >= 0 && index < officers.count {
return officers[index]
} else {
return `default`
}
}
}
let force = PoliceForce(officers: ["Amy", "Jake", "Rosa", "Terry"])
print(force[0])
print(force[5])
Esto imprimirá “Amy” y luego “Desconocido”, y este último se debe a que no hay ningún oficial en el índice 5. Tenga en cuenta que debe escribir las etiquetas de sus parámetros dos veces si desea que se utilicen, porque de lo contrario los subíndices no usan etiquetas de parámetros.
Entonces, como lo uso default defaulten mi subíndice, puedo usar un valor personalizado como este:
print(force[-1, default: "Default value"])
8. El orden de filtrado diferido ahora se ejecuta en el orden correcto
A partir de Swift 5.2 se introduce un pequeño cambio que modifica la funcionalidad anterior. Cuando aplicamos varios filtros a una secuencia diferida (lazy) como una matriz, el orden de ejecución de los filtros es descendente. Vea el siguiente ejemplo:
let people = ["Gina", "juan", "Stalingrado", "Strava"]
.lazy
.filter { $0.hasPrefix("S") }
.filter { print($0); return true }
_ = people.count
En Swift 5.2+ , se imprimirá “Stalingrado” y “Strava”, porque al ejecutarse el primer filtro, esos son los únicos nombres que quedan para incluir en el segundo filtro. Pero antes de Swift 5.2, habría devuelto los cuatro nombres, porque el segundo filtro se habría ejecutado antes que el primero. Esto era confuso, porque si eliminas la palabra lazy, el código siempre devolvería “Stalingrado” y “Strava, independientemente de la versión de Swift.
Este comportamiento sigue siendo problemático según la versión del Sistema Operativo en que se ejecute. Por ejemplo, si ejecuta el código Swift 5.2 en iOS 13.3 o anterior, o macOS 10.15.3 o anterior, entonces obtendrá el antiguo comportamiento (se devolverán los cuatro nombres ), pero el mismo código ejecutándose en sistemas operativos más nuevos dará el comportamiento nuevo y correcto(solo devolverá “Stalingrado” y “Strava)
9. Valores predeterminados de ámbitos externo
Ahora el compilador permite que funciones internas o locales admitan valores predeterminados que capturan valores del ámbito externo. Por ejemplo:
func suma(a : Int, b : Int){
print(tep(_a: 25))
func temp(_a : Int, _b : Int = b ){ //_b puede capturar el valor del parametro b
}
}
10. Métodos anulados (override) no pueden utilizar una firma genérica incorrecta
Ahora el compilador dará un error si intentamos anular un método con un genérico no válido. Por ejemplo:
protocolo P { }
class Base {
func doTask < T >( entrada : T ) { }
}
class Derivada : Base {
override func doTask < T : P >( entrada : T ) { } //ERROR! No coincide la firma genérica
}
11. Extensión de protocolo con restricción de clases
Ahora, una extensión de protocolo con restricciones de clase, en la que el protocolo extendido no impone una restricción de clase, inferirá la restricción de manera implícita. Vea el siguiente ejemplo:
protocol Shape {} // Protocolo que representa una forma geométrica
class Circle: Shape {
var radius: Double = 0 // Radio del círculo
}
extension Shape where Self: Circle {
var diameter: Double { // Diámetro del círculo
get { return radius * 2 }
set { radius = newValue / 2 }
}
}
Conclusiones:
Swift 5.2 introdujo mejoras significativas, como un motor de diagnóstico más preciso, optimizaciones de rendimiento y nuevas funcionalidades para la declaración de funciones. En resumen, esta versión hizo que Swift fuera un lenguaje aún más potente y fácil de usar para desarrollar aplicaciones iOS, macOS, watchOS y tvOS.
Si te ha gustado este contenido puedes dejarme un comentario o compartirlo en tus redes sociales para que otros se beneficien del mismo. Gracias por leer y feliz codificación!