Pular para o conteúdo

Boas práticas e padrões · Concorrência · 6 min de leitura

Concorrência em Swift: async/await, Task e actors

Swift traz concorrência estruturada na linguagem: async/await para esperar sem bloquear, Task para lançar trabalho, actors para proteger estado e o compilador checando o que pode cruzar threads.

View → @MainActor1@MainActor → await api.perfil()2@MainActor → actor Cache3await api.perfil() → estado atualizado4View — .task { }View.task { }@MainActor — modelo da tela@MainActormodelo da telaawait api.perfil() — suspendeawait api.perfil()suspendeactor Cache — um acesso por vezactor Cacheum acesso por vezestado atualizado — na mainestado atualizadona main

Arraste para o lado para ver o diagrama inteiro.

Cada await é um ponto onde a função pode pausar; o actor garante que só uma tarefa por vez mexe no cache.

async/await e Task

Uma função async pode suspender em cada await sem travar a thread. Para chamá-la a partir de código síncrono, crie uma Task { }; no SwiftUI, prefira o modificador .task { }, que cancela sozinho quando a view some. Para várias chamadas em paralelo, use async let ou withTaskGroup.

Actors e @MainActor

Um actor é como uma classe cujo estado só é acessado por uma tarefa de cada vez: acabam as condições de corrida nesse estado. Quem está fora precisa de await para chamá-lo. @MainActor marca o que deve rodar na thread principal — modelos de tela e atualização de UI.

Cuidado com a reentrância: depois de um await dentro do actor, o estado pode ter mudado. Releia antes de usar.

Sendable e Swift 6

Tipos Sendable podem cruzar fronteiras de concorrência com segurança (structs de valores, actors, classes finais imutáveis). No modo de linguagem Swift 6, o compilador transforma em erro o que antes era só risco de corrida — vale ativar e corrigir aos poucos.

No código: antes e depois

✗ AntesSwift
final class ImageCache {
    var images: [URL: UIImage] = [:] // acessado de várias threads: corrida

    func load(_ url: URL, done: @escaping (UIImage?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            let image = data.flatMap(UIImage.init)
            self.images[url] = image // escreve fora da main e sem proteção
            done(image)
        }.resume()
    }
}
✓ DepoisSwift
actor ImageCache {
    private var images: [URL: UIImage] = [:]

    func image(for url: URL) async throws -> UIImage {
        if let cached = images[url] { return cached }
        let (data, _) = try await URLSession.shared.data(from: url)
        guard let image = UIImage(data: data) else { throw URLError(.cannotDecodeContentData) }
        images[url] = image // só uma tarefa por vez chega aqui
        return image
    }
}

@MainActor @Observable final class AvatarModel {
    var image: UIImage?
    func load(_ url: URL, cache: ImageCache) async {
        image = try? await cache.image(for: url) // volta à main por ser @MainActor
    }
}

O actor protege o dicionário; o modelo @MainActor garante que a UI só é atualizada na thread principal.

Em resumo

Perguntar à IA

Monte um prompt pronto sobre Concorrência em Swift: async/await, Task e actors na sua linguagem e no seu caso, e abra num chat gratuito.

ChatGPT Claude Perplexity

O prompt vai para o site que você escolher. Não cole senhas, tokens nem dados reais de clientes. Para Gemini e outros, copie e cole.

Referências

Continue aprendendo

Todos os temas de boas práticas e padrões →