¿Por qué los hilos en segundo plano son el dolor de cabeza de cada desarrollador iOS?
\n\nImagine: está escribiendo una aplicación para descargar fotos de Internet. Todo funciona perfectamente... hasta que el usuario presiona el botón "Descargar". La pantalla se congela, la aplicación se "ralentiza" y, después de unos segundos, iOS simplemente mata su proceso. ¿Le suena familiar?
\n\nEl problema es que, por defecto, todas las operaciones se ejecutan en el hilo principal (main thread). Y el hilo principal es responsable de renderizar la UI; si lo bloquea con una tarea larga, la interfaz dejará de responder a los toques. ¿La solución? Usar background thread (hilo en segundo plano) en Swift.
\n\nEn este artículo, analizaremos cómo trabajar con hilos en segundo plano con ejemplos reales. Aprenderá sobre GCD, OperationQueue y el nuevo async/await de Swift 5.5+. ¡Vamos allá!
\n\n¿Qué es un hilo (thread) en Swift?
\n\nUn hilo es un proceso ligero que ejecuta el código del programa. Una aplicación iOS tiene un hilo principal (main thread) y muchos hilos en segundo plano. El hilo principal se encarga de la UI: actualiza la pantalla, maneja gestos, animaciones. Todo lo relacionado con la interfaz debe ocurrir solo en el hilo principal.
\n\nLos hilos en segundo plano (background threads) son necesarios para:
\n- \n
- Descargar datos de la red \n
- Trabajar con archivos (lectura/escritura) \n
- Cálculos complejos (procesamiento de imágenes, cifrado) \n
- Trabajar con bases de datos \n
Si intenta actualizar la UI desde un hilo en segundo plano, la aplicación fallará con un error, o verá un comportamiento extraño en la interfaz.
\n\nGCD (Grand Central Dispatch) — la forma clásica
\n\nGrand Central Dispatch (GCD) es una biblioteca de bajo nivel para la gestión de hilos, integrada en iOS/macOS. Funciona a través de colas (queues): coloca una tarea en una cola y el sistema decide en qué hilo ejecutarla.
\n\nDispatchQueue.main — hilo principal
\nSi necesita ejecutar código en el hilo principal (por ejemplo, actualizar un UILabel), use:
\n\nDispatchQueue.main.async {\n self.label.text = "¡Descarga completada!"\n}\n\nDispatchQueue.global() — hilo en segundo plano
\nPara tareas en segundo plano, use la cola global:
\n\nDispatchQueue.global(qos: .background).async {\n // Trabajo pesado\n let imageData = try? Data(contentsOf: url)\n \n // Volvemos al hilo principal para la UI\n DispatchQueue.main.async {\n self.imageView.image = UIImage(data: imageData!)\n }\n}\n\nEl parámetro qos (Quality of Service) determina la prioridad de la tarea:
- \n
.userInteractive— la más alta, para la UI \n .userInitiated— el usuario espera el resultado \n .utility— tareas largas (descarga de archivos) \n .background— baja prioridad (sincronización, copias de seguridad) \n
OperationQueue — más control
\n\nOperationQueue es una capa superior sobre GCD con capacidades adicionales:
\n- \n
- Cancelación de tareas \n
- Dependencias entre tareas \n
- Número máximo de hilos paralelos \n
Ejemplo de descarga de varias imágenes:
\n\nlet queue = OperationQueue()\nqueue.maxConcurrentOperationCount = 2 // No más de 2 tareas simultáneas\n\nlet operation1 = BlockOperation {\n // Descarga de la primera imagen\n sleep(2)\n print("Foto 1 descargada")\n}\n\nlet operation2 = BlockOperation {\n // Descarga de la segunda imagen\n sleep(3)\n print("Foto 2 descargada")\n}\n\n// operation2 comenzará solo después de que termine operation1\noperation2.addDependency(operation1)\n\nqueue.addOperations([operation1, operation2], waitUntilFinished: false)\n\nEsto es útil cuando necesita ejecutar tareas en un orden estricto o con limitación de recursos.
\n\nAsync/await moderno (Swift 5.5+)
\n\nA partir de Swift 5.5, Ap