Novedades en Swift 5.7

Novedades de Swift 5.7

Swift 5.7 trae consigo una serie de mejoras y nuevas características que hacen que el desarrollo de aplicaciones sea aún más eficiente y expresivo. En este artículo, exploraremos algunas de las novedades más significativas de esta versión, acompañadas de ejemplos de código para ilustrar su uso.

1.Forma de abreviada de desempaquetar opcionales con If:SE-0345

Swift introduce una forma abreviada para obtener los valores de opcionales en variables ocultas del mismo nombre:

				
					var color : String? = "rojo"

//Antes
if let color =  color {
    print(color)
}

//Ahora
if let color {
    print(color)
}
				
			

Este cambio no se aplica a las propiedades dentro de los objetos:

				
					struct Colores{
    let color : String?
}

let obj = Colores(color: "verde")

if let obj.color { //Error!
    //code...
}
//Forma correcta:
if let color =  obj.color { //OK!
    print(color)
}
				
			

2. Inferencia de tipos en cierres: SE-0326

Swift impementa una mejora sistancial en la inferencia de tipos en parámetros y tipos para cierres. Ahora es posible usar una sintaxis más abreviada:

				
					let values = [14,58,-25,69]

//Antes
let result = values.map{item -> Int in //<=
    if item < 0 {
        return 0
    }else{
        return item
    }
}

//Ahora
let result = values.map{item in
    if item < 0 {
        return 0
    }else{
        return item
    }
}

print(result)
				
			

3. Reloj, instante y duración: SE-0329

Swift introduce nuevas formas de hacer referencias a tiempos y duraciones. Se divide en tres componentes:

  • Los relojes (clock) representan una forma de medir el paso de l tiempo. Pueden ser continuos y suspendidos.
  • Los instantes representan momentos exactos en el tiempo.
  • Las duraciones representan cuanto tiempo ha transcurrido entre dos instante.

Una de las aplicaciones de esto ocurre dentro de la nueva API Task que tiene una variante para suspender su ejecución. Ahora es posible establecer el tiempo de inactividad en segundos:

				
					try await Task.sleep(until: .now + .second(1), clock: .continuos)
				
			

Además esta nueva API tiene la ventaja de poder especificar la tolerancia, lo que permite que el sistema espera un poco más allá de la fecha límite de suspensión para maximizar la efiencia energética.

				
					try await Task.sleep(until: .now + .seconds(1), tolerance: .seconds(0.5), clock: .continuous)
				
			

Los relojes son una forma de medir el consumo de tiempo. Por ejemplo, podemos medir cuanto ha demorado la exportación de un archivo:

				
					let clock = ContinuousClock()

let tiempo = clock.measure {
    // Trabajo complejo...
}

print("El tiempo que demoró es \(tiempo.components.seconds) segundos")
				
			

4. Expresiones regulares

Swift 5.7 incorpora una serie de mejoras relacionadas con las expresiones regulares. Algunas de las cuales son

  • Introduce un nuevo tipo Regex
  • Presenta una DLS potenciado por un generador de resultados para crear expresiones regulares
  • Agrega la capacidad de crear una expresión regular usando  /…/ en una cadena. Antes la única forma era usando Regex.
  • Agrega muchos nuevos algoritmos de procesamientos de cadenas usando expresiones regulares

Para más información puede cosultar este artículo.

5. Tipos concretos como valores por defecto para parámetros predeterminados: SE-0347

Esta mejora extiende la funcionalidad del uso de parámetros genéricos para aceptar valores predeterminados. Antes de Swift 5.7 esto arrojaba un error del compilador. Un ejemplo de este uso sería:

				
					func getNewSequence<T: Sequence>(from options: T = 1...49, count: Int = 7){
  print(Array(options.shuffled().prefix(count)))
}

//Ahora podemos llamar a la funci´ón con cualquier secuencia o dejar que tome el valor predeterminado
getNewSequence(from: ["uno","dos","tres","cuatro","cinco","seis"], count: 3)
//...
getNewSequence()
				
			

6. Concurrencia en código de nivel superior: SE-0343

Esto actualiza la compatibilidad de Swift con código de nivel superior. Piense en un archivo main.swift para un programa de línea de comandos en macOS; esta mejora permite que se utilice la concurrencia a este nivel.  Un ejemplo, tomado de este post, ilustra esta mejora:

				
					import Foundation
let url = URL(string: "https://hws.dev/readings.json")!
let (data, _) = try await URLSession.shared.data(from: url)
let readings = try JSONDecoder().decode([Double].self, from: data)
print("Found \(readings.count) temperature readings")
				
			

7. Uso de some en declaraciones de parámetros: SE-0341

Por ejemplo, podemos tener una función que verifique si un arreglo esta ordenado:

				
					func isSorted(array: [some Comparable]) -> Bool {
    array == array.sorted()
}
				
			

El tipo [some Comparable] indica una matríz de elementos que se ajustan al protocolo Comparable. El código genérico que se usaba anteriormente es más extenso y menos legible:

				
					func isSortedOld<T: Comparable>(array: [T]) -> Bool {
    array == array.sorted()
}
//También podríamos escribirlo como una extensión pero sería aún más larga:
extension Array where Element : Comparable{
    func isSorted()->Bool{
        self == self.sorted()
    }
}
				
			

8.Tipos de resultados opacos estructurales: SE-0328

Esto extiende los lugares donde podemos utilizar tipos de resultados opacos. Ahora podemos devolver tipos opacos o más de un tipo opaco a la vez:

				
					import SwiftUI

func showUserDetails() -> (some Equatable, some Equatable) {
    (Text("Username"), Text("@twostraws"))
}

func createUser() -> [some View] {
    let usernames = ["@frankefoster", "@mikaela__caron", "@museumshuffle"]
    return usernames.map(Text.init)
}

//Incluso podemos devolver funciones o cierres que devuelven tipos opacos:
func createDiceRoll() -> () -> some View {
    return {
        let diceRoll = Int.random(in: 1...6)
        return Text(String(diceRoll))
    }
}
				
			

9. Existenciales para todos los protocolos:SE-0309

Esto permite utilizar protocolos como tipos cuando tienen Self como requisito. Esto significa que un código asi es correcto:

				
					let color1: any Equatable = "rojo"
let color2: any Equatable = "verde"
				
			

En este caso el protocolo Equatable tiene requisitos de Self,  lo que significa que provee de una funcionalidad que hacer referencia al tipo específico que lo adopta. Para más información puede consultar este post.

10. Sintaxis más simple para hacer referencia a protocolos que declaran tipos específicos asociados: SE-0346

Veamos un ejemplo:

				
					protocol Cache<Content> {
    associatedtype Content

    var items: [Content] { get set }

    init(items: [Content])
    mutating func add(item: Content)
}
				
			

Observe como el protocolo tiene un tipo genérico: <Content>. Esto es lo que se llama tipo asociado que declara algún tipo que los tipos conformes deben llenar. Si desea profundizar más puede leer este post.

11. Tipos existenciales restringidos: SE-0353

Para más información consulte estos post:

12. Aislamiento de actores distribuidos: SE-0336 y SE-0344

Ahora es posible que los actores fucionen de forma distruida leyendo y escribiendo propiedades o llamar a métodos a través de una red utilizando RPC (Llamadas a Procedimientos Remotos).

 

13.BuildPartialBlock para generadores de resultados: SE-0348

Se simplica enormentente la sobrecarga necesaria para implementar generadores de resultados complejos. Si desea conocer más sobre este tema puede leer este post.

14. Existenciales abiertos implícitamente: SE-0352

Esta novedad permite a Swift llamar a funciones genéricas usando un protocolo o un existencial como parámetro en muchas situaciones, lo cual remueve una barrera que exista anteriormente.

Por ejemplo, ahora es posible esto:

				
					//Función genérica que puede trabajar con cualquier tipo Numeric 
func double<T: Numeric>(_ number: T) -> T {
    number * 2
}

//Con esta novedad podemos llamar a la funcion pasándole un protocolo (existencial):
let first = 1
let second = 2.0
let third: Float = 3

let numbers: [any Numeric] = [first, second, third]

for number in numbers {
    print(double(number))
}

				
			

15. Indisponibilidad del atributo async:SE-0340

Swift cierra la brecha a una sotuación riesgosa en el modelo de concurrencia de Swift. Esto permite marcar tipo y funciones como no disponibles en contextos asincrónicos debido a que su uso podría ser problemático. 

A menos que estes usando almacenamiento local de subprocesos, bloqueos, mutexes o semáforos, es poco probable que uses este atributo, pero podrías llamar a código que lo usa, por lo que es una opción a mano.

Para marcar algo como no disponible para un contexto asincrónico, utilice el atributo @available con su selección normal de la plataforma y luego agrege noasync al final.

Por ejemplo, podríamos tener una función común para varias plataformas pero que dará problemas si se la llama de manera asincrónica, por lo que la marcamos de este manera:

				
					@available(*, noasync)
func doRiskyWork() {

}
				
			

Ahora podemos llamar a esta función desde un contexto sincrónico:

				
					func synchronousCaller() {
    doRiskyWork()
}
				
			

Pero daría un error si intentamos hacer la llamada desde una función asincrónica:

				
					func asynchronousCaller() async {
    doRiskyWork() //Error!!
}
				
			

Esta protección es una mejora, pero puede ser evadido fácilmente si llamamos a la función sincrónica desde dentro de la función asincrónica, entonces se podría llamar a la función de riesgo en un contexto asincrónico:

				
					func sneakyCaller() async {
    synchronousCaller()
}
				
			

Conclusión:

Swift 5.7 representa un paso importante en la evolución del lenguaje, brindando a los desarrolladores herramientas más poderosas y expresivas. Estas nuevas características hacen que Swift sea aún más atractivo para el desarrollo de aplicaciones modernas.

Gracias por leer este artículo, espero que le sea útil. Feliz codificación!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio