O Legado e os Gargalos do Grand Central Dispatch (GCD)

Por mais de uma década, o Grand Central Dispatch (GCD) e as filas de despacho (DispatchQueue) foram o alicerce absoluto de concorrência e tarefas em segundo plano no ecossistema Apple. Desenvolvedores iOS acostumaram-se a despachar blocos com DispatchQueue.global().async para operações pesadas de I/O e retornar para DispatchQueue.main.async para atualizar elementos visuais na tela.

Apesar de sua eficiência em nível de sistema operacional (baseada em threads C da biblioteca libdispatch), o modelo baseado em closures e callbacks sofria de problemas crônicos bem conhecidos por engenheiros seniores:

  • Inferno de Callbacks (Callback Hell): Chamadas assíncronas em cascata resultavam em pirâmides de indentação difíceis de ler e auditar;
  • Vazamentos de Memória e Retain Cycles: O esquecimento de cláusulas de captura fraca ([weak self]) nas closures acumulava instâncias zumbis na memória volátil;
  • Explosão de Threads (Thread Explosion): O disparo desenfreado de filas concorrentes bloqueadas por semáforos levava o sistema a instanciar centenas de threads, esgotando a memória e a bateria do dispositivo;
  • Condições de Corrida Ocultas (Data Races): Duas threads concorrentes acessando a mesma variável compartilhada sem locks podiam corromper dados silenciosamente sem qualquer aviso do compilador em tempo de desenvolvimento.

A Revolução da Concorrência Moderna: O modelo introduzido a partir do Swift 5.5 e amadurecido no Swift 6 transforma concorrência em um recurso de primeira classe da linguagem, onde a segurança de acesso à memória (Data Race Safety) é garantida matematicamente em tempo de compilação.

A Elegância do async/await e Tratamento Estruturado de Erros

A substituição de callbacks por async/await permite redigir código assíncrono com a mesma linearidade e simplicidade de código síncrono. Erros deixam de exigir tipos complexos como Result<T, Error> em retornos de closures e passam a ser capturados pelo mecanismo nativo de try/catch:

// Abordagem Moderna: Linear, Clara e Segura
public final class AccountService {
    private let session: URLSession

    public init(session: URLSession = .shared) {
        self.session = session
    }

    public func fetchBalance(accountId: String) async throws -> AccountBalance {
        guard let url = URL(string: "https://api.empresa.com.br/v1/accounts/\(accountId)/balance") else {
            throw NetworkError.invalidURL
        }

        let (data, response) = try await session.data(from: url)

        guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
            throw NetworkError.serverError
        }

        return try JSONDecoder().decode(AccountBalance.self, from: data)
    }
}

Concorrência Estruturada com Task e TaskGroup

Ao disparar múltiplas operações simultâneas (por exemplo, carregar o extrato, a cotação de moedas e as notificações do usuário ao abrir o aplicativo), a Concorrência Estruturada (Structured Concurrency) assegura que todas as subtarefas pertençam a uma mesma árvore hierárquica.

Se o usuário sair da tela antes que as requisições terminem, a tarefa-pai é cancelada e o sinal de cancelamento se propaga automaticamente por todas as subtarefas filhas, poupando largura de banda de rede e ciclos de processador:

// Carregamento concorrente de múltiplos recursos com TaskGroup
public func loadDashboardData() async throws -> DashboardData {
    try await withThrowingTaskGroup(of: DashboardComponent.self) { group in
        group.addTask { .balance(try await self.fetchBalance()) }
        group.addTask { .cards(try await self.fetchCards()) }
        group.addTask { .insights(try await self.fetchInsights()) }

        var components: [DashboardComponent] = []
        for try await component in group {
            components.append(component)
        }
        return DashboardData(components: components)
    }
}

Proteção Contra Data Races com Actors e @MainActor

O tipo nominal actor é o pilar definitivo para sincronização de estado em Swift. Ao contrário de uma class tradicional, um Actor protege seu estado interno contra acessos concorrentes simultâneos sem a necessidade de instanciar semáforos manuais (NSLock ou os_unfair_lock):

// Estado isolado protegido contra condições de corrida por design
public actor SecuritySessionVault {
    private var activeSessions: [String: SessionToken] = [:]

    public func store(token: SessionToken, for userId: String) {
        // Acesso sequencial garantido pelo runtime do Swift
        activeSessions[userId] = token
    }

    public func token(for userId: String) -> SessionToken? {
        return activeSessions[userId]
    }
}

Para a camada de interface gráfica (UIKit / SwiftUI), o compilador oferece o atributo global @MainActor. Ele garante formalmente que métodos de atualização visual sempre executem na thread principal (Main Thread), eliminando em definitivo os temidos alertas de "UI API called from background thread":

@MainActor
public final class BalanceViewController: UIViewController {
    private let viewModel: BalanceViewModel

    public func updateView(with balance: String) {
        // Garantido em tempo de compilação que executa estritamente na Main Thread
        balanceLabel.text = balance
    }
}

Conclusão

A adoção integral de async/await, TaskGroup e Actors eleva a qualidade do código a um patamar incomparável de previsibilidade e robustez. Em projetos de alta exigência técnica, a migração para a concorrência moderna do Swift elimina dezenas de bugs intermitentes de concorrência antes mesmo de o aplicativo atingir os ambientes de teste em produção.