Programación asíncrona en Swift: guía completa para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a la programación asíncrona en Swift

\n\n

Las aplicaciones móviles modernas rara vez prescinden de solicitudes de red, trabajo con bases de datos o procesamiento de archivos grandes. Todas estas operaciones requieren tiempo, y si se ejecutan en el hilo principal, la interfaz de la aplicación se "congelará" y el usuario tendrá una mala experiencia. Aquí es donde la programación asíncrona viene al rescate.

\n\n

Antes de la llegada de Swift 5.5, los desarrolladores usaban closures (completion handlers) o bibliotecas de terceros (Combine, RxSwift), lo que a menudo llevaba al "infierno de los callbacks" y a un código difícil de leer. Con el lanzamiento del nuevo modelo Swift Concurrency (async/await), escribir código asíncrono se ha vuelto tan simple y lineal como el código síncrono.

\n\n

En esta guía, analizaremos los conceptos clave de la programación asíncrona en Swift con ejemplos prácticos. Aprenderás a usar async/await, Task, Actor y otras herramientas para crear aplicaciones rápidas y receptivas para iOS y macOS.

\n\n

Conceptos principales de Swift Concurrency

\n\n

1. Async y Await: la base de la asincronía

\n\n

Las palabras clave async y await son el corazón del nuevo modelo. async declara una función como asíncrona, y await pausa la ejecución hasta obtener el resultado, sin bloquear el hilo.

\n\n

Veamos un ejemplo simple: cargar datos desde la red.

\n\n
import Foundation\n\n// Declaramos una función asíncrona\nfunc fetchUserData() async throws -> String {\n    let url = URL(string: "https://api.example.com/user/1")!\n    let (data, _) = try await URLSession.shared.data(from: url)\n    return String(data: data, encoding: .utf8) ?? "Error"\n}\n\n// Llamada a la función asíncrona\nTask {\n    do {\n        let userData = try await fetchUserData()\n        print("Datos del usuario: \\(userData)")\n    } catch {\n        print("Error de carga: \\(error.localizedDescription)")\n    }\n}
\n\n

Observa: try await significa que esperamos un resultado que puede lanzar un error. El código se ve como síncrono normal, pero no bloquea el hilo principal.

\n\n

2. Task: punto de entrada al mundo asíncrono

\n\n

Las funciones asíncronas solo se pueden llamar desde otro contexto asíncrono. Para iniciar una operación asíncrona desde código síncrono (por ejemplo, desde viewDidLoad()) se usa Task.

\n\n
class ViewController: UIViewController {\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        // Iniciamos una tarea asíncrona\n        Task {\n            let data = await loadData()\n            updateUI(with: data)\n        }\n    }\n    \n    func loadData() async -> String {\n        // Simulación de carga larga\n        try? await Task.sleep(nanoseconds: 2_000_000_000)\n        return "Datos cargados"\n    }\n    \n    func updateUI(with data: String) {\n        print("Actualizando UI: \\(data)")\n    }\n}
\n\n

Importante: Task crea una nueva tarea asíncrona que se ejecuta en un hilo en segundo plano. Para actualizar la UI, asegúrate de volver al hilo principal; por defecto, await lo hace automáticamente.

\n\n

3. Actor: protección contra condiciones de carrera

\n\n

Al trabajar con multitarea, a menudo surge el problema de condición de carrera (data race), cuando varios hilos modifican una misma variable simultáneamente. Swift ofrece una solución elegante: Actor.

\n\n

Actor es un tipo por referencia que garantiza que solo un hilo puede acceder a su estado mutable en cualquier momento.

\n\n
actor Counter {\n    private var value = 0\n    \n    func increment() {\n        value += 1\n    }\n    \n    func getValue() -> Int {\n        return value\n    }\n}\n\n// Uso\nlet counter = Counter                

Blogs

Book Recommendations