Filas e tarefas em segundo plano em Swift: como fazer o aplicativo voar
\n\nImagine: você abre um aplicativo para pedir pizza, clica em "Ver cardápio" e a tela congela por cinco segundos. Você começa a ficar nervoso, a pizza parece sem graça, o aplicativo vai para o lixo. Dor familiar? Se você é desenvolvedor Swift, provavelmente já enfrentou situações em que a interface "trava" devido a operações pesadas — carregamento de imagens, parsing de JSON ou cálculos complexos. O segredo de uma UX fluida está no trabalho correto com filas e tarefas em segundo plano.
\n\nNeste artigo, vamos abordar como organizar o multithreading em Swift: desde o clássico GCD e OperationQueue até o moderno async/await. Você aprenderá como descarregar o trabalho pesado da thread principal, evitar deadlocks e escrever código que funcione de forma rápida e previsível. Vamos lá!
Por que fila não é sobre tédio no supermercado
\n\nEm Swift, fila não é o lugar onde você fica com o carrinho. Filas (DispatchQueue) são um mecanismo para gerenciar tarefas. Imagine que você tem vários cozinheiros (threads). Um cozinheiro atende o salão (thread principal — UI), e os outros cozinham na cozinha (threads em segundo plano). Se você fizer o garçom grelhar um bife, o salão para. O mesmo acontece com a interface: todas as atualizações de UI devem ocorrer apenas na thread principal, e todo o resto, em segundo plano.
\n\nEm Swift, existem dois tipos de filas:
\n- \n
- Serial (sequencial) — as tarefas são executadas uma após a outra. Ideal para proteger recursos compartilhados. \n
- Concurrent (paralela) — as tarefas iniciam simultaneamente. Ótimo para carregar imagens ou cálculos paralelos. \n
O sistema gerencia o pool de threads automaticamente, então você não precisa criá-las manualmente. Basta dizer: "Faça isso em segundo plano e depois retorne o resultado para a thread principal". E o Swift faz isso de forma elegante.
\n\nGCD (Grand Central Dispatch) — um velho amigo é melhor que dois novos
\n\nGrand Central Dispatch é uma API de baixo nível que surgiu ainda no Objective-C. Ela está viva e saudável, e vale a pena todo desenvolvedor Swift conhecê-la. O trabalho principal ocorre através de DispatchQueue.
Aqui está o exemplo mais simples — carregar dados em segundo plano e atualizar a UI:
\n\nimport UIKit\n\nclass ViewController: UIViewController {\n @IBOutlet weak var imageView: UIImageView!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n loadImage()\n }\n \n func loadImage() {\n // Vamos para a fila em segundo plano\n DispatchQueue.global(qos: .background).async {\n // Trabalho pesado: carregamento de imagem\n guard let url = URL(string: "https://example.com/pizza.jpg"),\n let data = try? Data(contentsOf: url),\n let image = UIImage(data: data) else {\n return\n }\n \n // Retornamos à thread principal para atualizar a UI\n DispatchQueue.main.async {\n self.imageView.image = image\n }\n }\n }\n}\n\n\nObserve o qos (Quality of Service). É a prioridade da tarefa: .userInteractive — para animações, .background — para sincronização, .utility — para indicadores de progresso. A escolha correta do QoS ajuda o sistema a distribuir recursos.
DispatchGroup: esperamos todos, não apenas um
\n\nImagine que você precisa carregar três imagens e só depois exibi-las. Sem DispatchGroup, você iniciaria três requisições assíncronas e não saberia quando todas terminariam. A solução é o grupo:
func loadMultipleImages() {\n let group = DispatchGroup()\n var images: [UIImage] = []\n \n let urls = ["url1", "url2", "url3"]\n \n for urlString in urls {\n group.enter() // Entra no grupo\n DispatchQueue.global().async {\n // Carregamento