@MainActor é um ator global que executa suas tarefas na thread principal. Você pode usá-lo para enviar tarefas para a thread principal marcando propriedades, métodos, instâncias ou closures com o atributo. Em vez de fazer a chamada manualmente usando DispatchQueue.main.async, você permite que o compilador forneça a execução na thread principal para você.
Se você é novo em Atores em Swift, recomendo ler meu artigo Atores em Swift: como usar e prevenir corridas de dados. O ator principal se comporta como outros atores, com uma diferença importante: ele executa todas as suas tarefas na thread principal.
O que é um MainActor?
Um MainActor é um ator global único que executa suas tarefas na thread principal. Você pode usá-lo para propriedades, métodos, instâncias e closures para executar tarefas na thread principal. A proposta SE-0316 Global Actors introduziu o ator principal como um exemplo de ator global que herda do protocolo GlobalActor.
Você ainda precisa de @MainActor em Swift 6.2 e posterior?
Desde o Swift 6.2, seu código pode já estar rodando no ator principal sem nenhuma anotação. Novos projetos no Xcode 26 e posteriores habilitam Default Actor Isolation, que isola todo o código dentro do seu alvo de aplicativo ao ator principal a menos que você opte por não fazê-lo. Em outras palavras: se você está trabalhando em um novo projeto, geralmente não precisa adicionar @MainActor manualmente mais.
Esta mudança é parte de um conjunto maior de melhorias que abordei em Concorrência Acessível no Swift 6.2: Uma Guia Clara. Para este artigo, é importante saber que o atributo ainda funciona exatamente da mesma maneira. Seja você escrevendo @MainActor ou o compilador inferindo-o, as mesmas regras se aplicam.
MainActor: um Ato global da biblioteca padrão
O MainActor é conhecido como ator global: um ator com uma instância única e compartilhada globalmente. Sua implementação está disponível por padrão e se parece com o seguinte:
@globalActor
final actor MainActor: GlobalActor {
static let shared: MainActor
}
Qualquer lugar onde você use o atributo @MainActor, você sincronizará através dessa instância do ator compartilhado, garantindo acesso mutuamente exclusivo às declarações. Você também pode definir atores globais personalizados, conforme explicado em Ator global na concorrência Swift explicado com exemplos de código.
Como usar MainActor em Swift?
Você pode usar um ator global com propriedades, métodos, closures e instâncias. Por exemplo, podemos adicionar o atributo @MainActor a uma classe de view model para executar todas as tarefas na thread principal:
@MainActor
final class HomeViewModel {
// ..
}
Usando nonisolated, garantimos que métodos sem a necessidade da thread principal sejam executados o mais rápido possível, sem esperar pela disponibilidade da thread principal. Você pode anotar uma classe com um ator global apenas se ela não tiver superclass, a superclass estiver anotada com o mesmo ator global ou a superclass for NSObject. Uma subclasse de uma classe anotada com ator global deve ser isolada ao mesmo ator global.
Em outros casos, podemos querer definir propriedades individuais com um ator global:
final class HomeViewModel {
@MainActor var images: [UIImage] = []
}
Marcar a propriedade images com o atributo @MainActor garante que ela só possa ser atualizada na thread principal:

Isto é ótimo ao trabalhar com MVVM em SwiftUI pois você só deseja disparar redesenhos de visualização na thread principal.
Você pode marcar métodos individuais com o atributo também:
@MainActor func updateViews() {
/// Será sempre despachado para a thread principal, desde que seja chamado
/// dentro de um contexto de concorrência.
}
Cuidado: este método só é garantido para rodar na thread principal quando você o chama de um contexto de concorrência. O modo de linguagem Swift 6 pega a maioria desses casos em tempo de compilação. Eu vou mergulhar mais fundo nessa armadilha posteriormente neste artigo.
func atualizarDados(completion: @MainActor @escaping () -> ()) {
Task {
await someHeavyBackgroundOperation()
await completion()
}
}
Embora neste caso, você deva reescrever o método atualizarDados como uma variante assíncrona sem a necessidade de um fechamento de conclusão.
Usando o ator principal diretamente
O MainActor em Swift vem com uma extensão para usar o ator diretamente:
extension MainActor {
/// Executa a closure fornecida no ator principal.
public static func run<T>(resultType: T.Type = T.self, body: @MainActor @Sendable () throws -> T) async rethrows -> T
}
Isso nos permite chamar o @MainActor em métodos sem usar seu atributo no corpo.
Task {
await someHeavyBackgroundOperation()
await MainActor.run {
// Realizar atualizações na UI
}
}
Em outras palavras, não há mais necessidade de usar DispatchQueue.main.async. No entanto, recomendo o uso do atributo global para restringir qualquer acesso à thread principal. Sem o atributo global do ator, alguém poderia esquecer de usar MainActor.run, potencialmente levando a atualizações na UI ocorrendo em uma thread secundária.
MainActor.run vs. Task { @MainActor in }
Outra maneira comum de despachar para o principal é isolando a closure da tarefa:
Task { @MainActor in
imageView.image = image
}
A diferença com MainActor.run é sutil, mas essencial. Uma tarefa com uma closure isolada do ator principal executa todo o seu trabalho no ator principal, enquanto MainActor.run apenas faz a transição para a closure fornecida:
Task {
/// Executa em algum lugar no background
let image = try await downloadImage()
/// Faz a transição explícita para o ator principal
await MainActor.run {
imageView.image = image
}
}
Eu prefiro usar MainActor.run quando apenas parte da minha tarefa requer execução na thread principal. Se toda a tarefa é sobre atualizar a UI, eu isolo a closure usando Task { @MainActor in } em vez disso.
Verificando a execução do ator principal com assumeIsolated
Nos casos raros em que você sabe com certeza que seu código está sendo executado na thread principal, enquanto o compilador não pode verificar isso, você pode usar MainActor.assumeIsolated para acessar o código isolado do ator principal a partir de um contexto síncrono e não isolado:
nonisolated func handleCallback() {
MainActor.assumeIsolated {
updateViews()
}
}
Cuidado: assumeIsolated não faz a chamada para a thread principal por você. Em vez disso, ele interrompe a execução do programa quando é chamado de qualquer outra thread. Em outras palavras, use-o apenas quando estiver 100% certo de que seu código já está sendo executado na thread principal.
Quando devo adicionar a atribuição @MainActor?
Sempre que você deve adicionar essa atribuição depende da configuração do seu projeto. Se o seu projeto usa o isolamento padrão do ator em Swift 6.2, grande parte do seu código já está isolado ao ator principal. A pergunta se torna a oposta: quando devo optar por sair disso? Você pode usar nonisolated ou @concurrent para qualquer código que não deve bloquear a thread principal, como tarefas de processamento pesado.
Nos projetos sem isolamento padrão, recomendo adicionar @MainActor em qualquer tipo ou método que atualize a UI. Antes da Swift Concurrency, você pode ter escrito várias declarações de dispatch para garantir que os callbacks chegassem na thread principal. Ao reescrever seu código para usar async/await e o ator principal, você permite que o dispatching ocorra apenas quando necessário:
@MainActor
func fetchImage(for url: URL) async throws -> UIImage {
let (data, _) = try await URLSession.shared.data(from: url)
guard let image = UIImage(data: data) else {
throw ImageFetchingError.imageDecodingFailed
}
return image
}
A atribuição @MainActor garante que a lógica seja executada na thread principal, enquanto a solicitação de rede ainda é realizada em uma thread de fundo. O dispatching para o ator principal ocorre apenas quando necessário, garantindo assim o melhor desempenho possível.
Pode anotar um ator com @MainActor?
Não, e o compilador informará que um ator não pode ter um atributo de global actor. Um ator define sua própria isolamento, o que conflita em isolar a ele ao ator principal global. Se você precisar de um tipo que execute todo seu trabalho na thread principal, deve usar uma classe anotada com @MainActor.
Entendendo Por Que @MainActor Não Garante Sempre a Execução na Thread Principal
Se você é novo em concorrência, pode pensar que anotar um método com @MainActor garante sempre que ele será executado na thread principal. Infelizmente, isso não é garantido para métodos síncronos em contextos não isolados. Várias equipes descobriram isso da maneira difícil, assim como Cyril Cermak, que me contatou durante uma de minhas visitas a conferências.
Antes de compartilhar os detalhes, é bom saber que o modo de linguagem Swift 6 captura a maioria dessas ocasiões. No entanto, como muitas equipes ainda estão migrando para Swift 6, queria destacar essa confusão.
Métodos síncronos em contextos não isolados são executados na mesma thread que o chamador, independentemente de quaisquer anotações de ator. Dependendo do modo de linguagem Swift que você está usando, pode ou não ser avisado sobre possíveis problemas.
Por exemplo, este código compila sem problemas no modo de linguagem Swift 5:

A chamada via a fila global cria um contexto não isolado e o atributo @MainActor não é respeitado.
No entanto, muitos projetos ainda são construídos com Swift 5, então é uma armadilha essencial para estar ciente.

Em projetos maiores, é provável que uma cadeia de métodos seja chamada em um thread de fundo. Eventualmente, essa cadeia pode terminar chamando um método atribuído ao ator principal enquanto é disparado sincronamente para o mesmo thread do chamador: um thread de fundo. Portanto, veja se você pode atualizar seu código para Swift 6; caso contrário, seja cuidadoso ao usar o atributo @MainActor.
Você pode não encontrar problemas diretamente se sempre chamar o método sincrono a partir de uma fonte que está rodando no thread principal. No entanto, é essencial estar ciente dessa diferença sutil, pois suas expectativas podem não se alinhar, resultando em falhas inesperadas relacionadas à UI antes da migração para Swift 6.
Continuando sua jornada na concorrência em Swift
As mudanças de concorrência são mais do que apenas atores e incluem outras funcionalidades que você pode aproveitar no seu código. Então, enquanto está nisso, por que não mergulhar nas outras funcionalidades de concorrência?
Conclusão
O atributo @MainActor garante que seu código seja executado no thread principal, o que é essencial para atualizar a UI em seus aplicativos. Você pode aplicá-lo a propriedades, métodos, instâncias e closures, e desde Swift 6.2, novos projetos até mesmo aplicam isso por padrão. Métodos como MainActor.run e assumeIsolated fornecem controle direto em casos onde anotações não se encaixam.
A isolamento é um desses conceitos que só faz sentido quando você os vê aplicados em um projeto real. É exatamente isso que faço no meu Curso de Concorrência Swift, onde você ficará confiante com atores e isolamento antes de começar a migração para o Swift 6.
Se quiser aprender mais dicas sobre Swift, verifique a página da categoria Swift. Sinta-se à vontade para entrar em contato comigo ou me enviar um tweet no Twitter se você tiver alguma dica adicional ou feedback.
Obrigado!

