Introducción: ¿Por qué necesitamos colas de tareas en Swift?
En el desarrollo moderno para iOS y macOS, el rendimiento de una aplicación depende directamente de qué tan eficientemente gestionas los hilos. Las operaciones largas —cargar datos de la red, procesar imágenes, trabajar con la base de datos— no deben bloquear el hilo principal (Main Thread), que es responsable de la interfaz. Si el hilo principal está ocupado, el usuario ve una pantalla "congelada" y el sistema puede cerrar la aplicación forzosamente.
Las colas de tareas (Task Queues) resuelven este problema. Permiten ejecutar código de forma asíncrona, paralela o secuencial, sin bloquear la UI. En Swift, hay tres herramientas principales para esto: Grand Central Dispatch (GCD), OperationQueue y el modelo moderno async/await (Swift Concurrency). En este artículo, las analizaremos con ejemplos prácticos y determinaremos cuándo usar cada una.
1. Grand Central Dispatch (GCD): el enfoque clásico
GCD es una biblioteca de bajo nivel en C que gestiona un grupo de hilos. En Swift, es fácil trabajar con ella a través de colas globales (DispatchQueue).
1.1. Tipos principales de colas en GCD
- Main Queue — cola secuencial del hilo principal. Se usa para actualizar la UI.
- Global Queues — colas paralelas con diferente prioridad (QoS:
.userInteractive,.userInitiated,.utility,.background). - Custom Queues — colas creadas por ti (pueden ser secuenciales o paralelas).
1.2. Ejemplo: cargar datos en segundo plano y actualizar la UI
Supongamos que necesitamos cargar un JSON desde un servidor remoto y mostrar el resultado en una tabla.
DispatchQueue.global(qos: .userInitiated).async { // 1. Trabajo en segundo plano: carga de datos guard let url = URL(string: "https://api.example.com/data") else { return } let data = try? Data(contentsOf: url) // 2. Volvemos al hilo principal para actualizar la UI DispatchQueue.main.async { // Actualizamos el modelo de datos o la tabla self.tableView.reloadData() }}1.3. Uso de DispatchGroup para sincronización
Cuando necesitas ejecutar varias tareas paralelas y esperar a que todas terminen, usa DispatchGroup.
let group = DispatchGroup()var images: [UIImage] = []
for url in imageURLs { group.enter() DispatchQueue.global().async { if let data = try? Data(contentsOf: url), let image = UIImage(data: data) { images.append(image) } group.leave() }}
group.notify(queue: .main) { print("Todas las imágenes cargadas: \\(images.count)") // Actualizamos la UI}2. OperationQueue: un nivel de abstracción más alto
OperationQueue está construida sobre GCD, pero ofrece capacidades adicionales: cancelación de tareas, dependencias entre operaciones, control del número máximo de operaciones paralelas.
2.1. Creación y ejecución de operaciones
Las operaciones se pueden crear mediante BlockOperation o heredando de Operation.
let queue = OperationQueue()queue.maxConcurrentOperationCount = 2 // Limitamos a 2 tareas paralelas
let operation1 = BlockOperation { print("Operación 1 iniciada") sleep(2) print("Operación 1 completada")}
let operation2 = BlockOperation { print("Operación 2 iniciada") sleep(1) print("Operación 2 completada")}
// Establecemos una dependencia: operation2 comenzará solo después de operation1operation2.addDependency(operation1)
queue.addOperations([operation1, operation2], waitUntilFinished: false)2.2. Cancelación de operaciones
A diferencia de GCD, las operaciones se pueden cancelar. Esto es crítico si el usuario sale de la pantalla, pero