Async/Await en Swift 5.5

3
3236

Índice de contenidos


1. Introducción

Con la release de Swift 5.5 se introdujo una de las características del lenguaje que los desarrolladores llevábamos más tiempo esperando, async/await. Muy similares al concepto de las suspend functions de Kotlin, nos permite tratar código asíncrono como si fuese síncrono, lo cual aumenta de una forma increíble la legibilidad del código.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15″ (2.5 GHz Intel Core i7, 16 GB 1600 MHz DDR3)
  • Sistema Operativo: macOS 12.1 Monterey
  • Xcode 13.2
  • Swift 5.5


3. De dónde venimos…

Hasta iOS 13 que se introdujo la librería Combine, todo el API del SDK de iOS empleaba callbacks para las operaciones asíncronas, de forma que por ejemplo para realizar una petición rest tendríamos que utilizar algo como esto.

El problema de esto es que en cuanto había que encadenar multiples peticiones, acabábamos sufriendo el llamado «callback hell», «pyramid of doom», u otros nombres igual de poco alentadores.

Callback hell

Para solventar esta problemática surgieron varias librerías y soluciones de terceros, como las promesas, RxSwift, etc. Con la llegada de iOS 13 llegaron Swift UI, y puesto que se trata de una capa de presentación declarativo y reactivo, junto a ello llego Combine, una librería reactiva, y lo anterior podía transformarse en algo como esto:

El principal «problema» de Combine es que tiene una curva de aprendizaje inicial muy pronunciada, y cuesta cogerle el truco al principio. Ademas la cosa puede ponerse fea muy rápidamente, en estos ejemplos no se están manejando errores ni se depende de valores anteriores para realizar nuevas requests…


4. Async/await al rescate

Con async/await los dos ejemplos anteriores se reducen a esto:

¿Sorprendido?

Otra forma de escribir el mismo fragmento seria la siguiente (nota: por algún motivo no funciona en un playground en el momento de escribir este tutorial):

En este caso hay una diferencia sutil, mientras el ejemplo anterior realiza la petición de red y espera, en este segundo caso se realizan las peticiones de forma concurrente y se espera al resultado de ambas para continuar.


5. Transformando a async/await

En el caso que tengamos un fragmento de código que utilice callbacks, combine o cualquier otra estrategia para ejecutar código asíncrono, podemos transformarlo a async/await. Por ejemplo, no existe una version async de dataTask en iOS < 15, para esos podemos transformarla de la siguiente forma:

Lo que hace es emplear un patrón visitor de forma que mediante el objeto continuation se indica el resultado de la tarea asíncrona. Hay que tener especial cuidado de cubrir toda la casuística o el hilo del que dependa la tarea no despertará.


6. Cómo funciona

Lo que hace swift internamente es que cada vez que se invoca algo con await, se suspende el hilo y se espera a que la tarea asíncrona finalice. Cuando la tarea de al que depende se completa esta llama a resume y hace que el hilo se despierte. En el punto anterior se puede ver este mecanismo claramente.

await


7. Agrupando operaciones

Un caso de uso común que se nos puede presentar es tener que realizar nuevas operaciones que dependen de un resultado anterior. Tomemos el siguiente ejemplo, tenemos un endpoint que nos devuelve Posts, con su título y una lista de IDs de comentarios, y por otro lado un endpoint que nos devuelve un comentario concreto por su id. Si queremos obtener todos los posts y sus comentarios podríamos hacer lo siguiente:

Con este mecanismo se añaden todas las operaciones a un grupo, que las irá ejecutando en paralelo. Luego es simplemente iterar sobre las operaciones del grupo y esperar a que terminen.


8. Conclusiones

El mecanismo de async/await es una mejora bastante importante del lenguaje. Hace que sea bastante más fácil de seguir el flujo de nuestro código y también lo hace mas legible, y se venía echando en falta y era muy demandado sobre todo después de haber trabajado con las suspend functions de kotlin o con javascript.

3 Comentarios

    • Buenas Vicente, los fragmentos de código son meramente ilustrativos, para este tutorial se utilizo un Playground, asi que el URLSession esta simplemente declarado justo antes que las funciones:

      let urlSession = URLSession(configuration: .default)
      var cancellables = Set()

      Bien podrían ser properties de una clase que albergase esos métodos

Dejar respuesta

Please enter your comment!
Please enter your name here