.task { } — o que é, modificador async e carregamento de dados em View

Autor: IT Sectr Publicado: 2026-06-26 Tempo de leitura: 9 min

.task { } é um modificador no SwiftUI introduzido no iOS 15 que inicia uma operação assíncrona quando uma View aparece e a cancela automaticamente quando a View desaparece. Ao contrário do .onAppear, que executa código síncrono sem possibilidade de cancelamento, o .task trabalha com o contexto async/await e respeita o ciclo de vida da View: quando a View desaparece, o SwiftUI chama cancel() no Task criado. Isso evita vazamentos de memória e a execução de operações depois que a View não precisa mais ser atualizada. De acordo com a Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024), o .task é a forma preferida de carregar dados no SwiftUI porque funciona com segurança com Structured Concurrency e gerencia automaticamente o tempo de vida da operação assíncrona.

Pontos Principais

  • .task { } — modificador SwiftUI para carregamento assíncrono de dados ao aparecer uma View, disponível desde iOS 15.
  • Cancelamento automático — quando a View desaparece, SwiftUI cancela o Task, evitando vazamentos de memória.
  • Contexto async/await — dentro de .task, chamadas async estão disponíveis sem necessidade de DispatchQueue ou Combine.
  • .task(id:) — variante com identificador que reinicia a tarefa quando o valor especificado muda.
  • Structured Concurrency — .task suporta Structured Concurrency e TaskGroup para operações paralelas.

O que é .task { } no SwiftUI

.task { } é um modificador de View que cria um Task em um contexto async quando a View aparece na tela. O SwiftUI executa o fechamento fornecido em uma thread de fundo, deixando a thread principal livre para operações de UI. Quando a View desaparece, o SwiftUI cancela automaticamente o Task através do mecanismo Structured Concurrency — isso garante que a operação assíncrona não continue executando depois que seu resultado não for mais necessário.

De acordo com Apple — Swift Programming Language (2025), o .task usa o conceito de Structured Concurrency introduzido no Swift 5.5. Cada .task cria uma tarefa filha dentro da tarefa da View pai. Se a tarefa pai for cancelada (a View desaparece), todas as tarefas filhas também são canceladas automaticamente. Isso simplifica radicalmente o gerenciamento do ciclo de vida de operações assíncronas em comparação com o armazenamento manual de referências a DispatchWorkItem ou AnyCancellable.

Ao contrário da abordagem tradicional com @State + chamadas manuais em .onAppear, o .task não requer armazenar uma referência ao Task para cancelamento posterior. O SwiftUI faz isso automaticamente, reduzindo código boilerplate e eliminando o risco de esquecer de cancelar uma tarefa.

swift
struct ArticlesView: View {
    @State var articles: [Article] = []
    @State var error: Error?
    
    var body: some View {
        List(articles) { article in
            Text(article.title)
        }
        .task {
            do {
                articles = await APIClient().fetchArticles()
            } catch {
                self.error = error
            }
        }
    }
}

.task vs .onAppear: diferenças principais

Muitos desenvolvedores estão acostumados a carregar dados em .onAppear, mas com a chegada de async/await e .task, esta abordagem se tornou obsoleta. .onAppear executa código de forma síncrona — para operações async dentro de .onAppear, é necessário envolver a chamada em Task { } e manter manualmente uma referência para possível cancelamento. .task faz isso automaticamente.

Característica.task { }.onAppear
Contexto asyncAsync/await integradoRequer wrapper Task { }
Auto-cancelamentoSim, quando a View desapareceNão, deve ser implementado manualmente
Structured ConcurrencySuportaNão suporta
Re-execuçãoApenas quando id mudaToda vez que a View aparece
Recomendação AppleAbordagem preferidaPara operações síncronas

.onAppear ainda é útil para operações síncronas — por exemplo, registro ou configuração inicial de UI. Mas para carregamento assíncrono de dados, requisições de rede, operações com banco de dados ou sistema de arquivos, use .task. É mais seguro e limpo do ponto de vista arquitetural.

swift
// ❌ Abordagem legada: Task em .onAppear sem cancelamento
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
    .onAppear {
        loadTask = Task { await loadData() }
    }
    .onDisappear { loadTask?.cancel() }

// ✅ Abordagem moderna: .task gerencia cancelamento
func body() { var body: some View { Text("") }
    .task { await loadData() }

.task(id:) — reiniciar ao alterar dados

O modificador .task(id:) aceita um parâmetro adicional — um identificador. Quando o valor do identificador muda, o SwiftUI cancela a tarefa atual e inicia uma nova com o novo identificador. Isso é ideal para telas onde os dados dependem de um parâmetro selecionado — por exemplo, uma lista de artigos por categoria ou detalhes de um produto por ID.

swift
struct CategoryView: View {
    let categoryId: Int
    @State var items: [Item] = []
    
    var body: some View {
        List(items) { item in
            Text(item.name)
        }
        .task(id: categoryId) {
            await loadItems(for: categoryId)
        }
    }
    
    func loadItems(for id: Int) async {
        do {
            items = await APIClient().fetchItems(categoryId: id)
        } catch {
            // tratar erro
        }
    }
}

Quando categoryId muda, o SwiftUI cancela a requisição anterior e inicia uma nova. Isso é especialmente importante para mudanças rápidas de categoria — requisições antigas não competirão com as novas pela atualização do estado. Sem .task(id:), você teria que rastrear manualmente as mudanças através de .onChange e gerenciar o Task manualmente.

Cancelamento de tarefas e verificação isCancelled

Embora .task cancele automaticamente a tarefa quando a View desaparece, a operação assíncrona em si deve verificar cooperativamente o cancelamento. O Swift usa um modelo de cancelamento cooperativo — Task.cancel() não interrompe a execução à força, mas apenas define a flag isCancelled. O código dentro da tarefa deve verificar periodicamente esta flag.

swift
struct LoadingView: View {
    @State var progress: Double = 0
    
    var body: some View {
        ProgressView(value: progress)
            .task {
                for i in 0..<100 {
                    // Verificar cancelamento
                    try Task.checkCancellation()
                    
                    await Task.sleep(nanoseconds: 50_000_000)
                    progress = Double(i + 1) / 100.0
                }
            }
    }
}

Task.checkCancellation() lança CancellationError se a tarefa foi cancelada. Esta é a forma mais simples de verificação — funciona em qualquer contexto async. Uma alternativa é verificar Task.isCancelled manualmente antes de operações custosas. Para URLSession, as requisições de rede são canceladas automaticamente quando a tarefa é cancelada, já que URLSession suporta Structured Concurrency nativamente.

Exemplos práticos de .task

.task é adequado para muitos cenários: desde carregamento simples de JSON até operações paralelas complexas com TaskGroup. Vamos ver três casos de uso típicos.

Carregamento com tratamento de erros

swift
struct ProfileView: View {
    @State var profile: Profile?
    @State var isLoading = true
    
    var body: some View {
        Group {
            if isLoading {
                ProgressView()
            } else if let profile {
                Text(profile.name)
            } else {
                Text("Falha ao carregar")
            }
        }
        .task {
            defer { isLoading = false }
            do {
                profile = await APIClient().fetchProfile()
            } catch {
                // perfil permanece nil
            }
        }
    }
}

Carregamento paralelo com TaskGroup

swift
struct DashboardView: View {
    @State var stats: DashboardStats?
    
    var body: some View {
        Text("Painel")
            .task {
                stats = await Task {
                    await withThrowingTaskGroup { group in
                        group.addTask { await API().fetchUsers() }
                        group.addTask { await API().fetchOrders() }
                        group.addTask { await API().fetchRevenue() }
                        
                        return DashboardStats(
                            users: try await group.next(),
                            orders: try await group.next(),
                            revenue: try await group.next()
                        )
                    }
                }.value
            }
    }
}

Erros comuns com .task

O erro mais comum é mutar propriedades de UI dentro de .task sem alternar para a thread principal. Embora o SwiftUI retorne automaticamente as atualizações para a thread principal ao modificar @State em um contexto async, manipulações diretas de elementos UIKit dentro de .task podem causar um crash.

  • Esquecer try/catch — .task não trata erros automaticamente. Todas as funções que lançam erros devem estar envolvidas em do/catch, caso contrário o aplicativo falhará.
  • Condição de corrida — se vários .task(id:) são iniciados com ids diferentes e atualizam o mesmo estado, podem ocorrer condições de corrida. Use propriedades separadas para diferentes fontes de dados.
  • Operações síncronas longas — .task não torna código síncrono assíncrono. Se houver trabalho síncrono pesado dentro de .task, envolva-o em Task.detached ou mova-o para um método async separado.
  • Ignorar CancellationError — ao verificar Task.checkCancellation(), o CancellationError deve ser propagado para cima, não suprimido. Suprimir o cancelamento pode levar a vazamentos de memória.
swift
// ❌ Erro: sem tratamento de erros
.task {
    let data = await fetchData() // falha ao ocorrer erro!
    items = data
}

// ✅ Correto: do/catch
.task {
    do {
        items = await fetchData()
    } catch {
        errorMessage = error.localizedDescription
    }
}

Perguntas Frequentes

Qual é a diferença entre .task e usar Task { } em .onAppear?

.task cancela automaticamente a tarefa quando a View desaparece e suporta Structured Concurrency. Task { } em .onAppear requer manter manualmente uma referência à tarefa e chamar cancel() em .onDisappear. .task também é mais fácil de ler — ele indica explicitamente que o carregamento de dados faz parte do ciclo de vida da View.

Pode-se usar .task para se inscrever em um publisher?

Sim, para se inscrever em um AsyncSequence ou AsyncStream, use for await value in publisher.values dentro de .task. Isso funciona tanto com async/await quanto com Combine através da extensão Publisher.values. Quando a View desaparece, a iteração terminará automaticamente e a inscrição será cancelada.

Como .task funciona com TabView — a tarefa reinicia ao trocar de aba?

Sim, se a View dentro de TabView for recriada em cada troca. A partir do iOS 18, o TabView pode manter Views na memória — nesse caso .task não reinicia. Use .task(id:) com um identificador de aba se precisar recarregar dados a cada troca de aba.

O que acontece se uma View com .task desaparecer antes da requisição ser concluída?

SwiftUI cancelará a tarefa quando a View desaparecer. Se a requisição URLSession estava dentro da tarefa, ela também será cancelada. Se a tarefa não suporta cancelamento cooperativo (por exemplo, não verifica isCancelled), ela continuará executando, mas seu resultado não será aplicado ao estado porque a View não existe mais.

Podem-se usar múltiplos modificadores .task em uma única View?

Sim, você pode adicionar vários modificadores .task a uma única View. Cada um cria uma tarefa independente. Isso é útil para separar diferentes fontes de dados: um .task para carregar um perfil, outro para se inscrever em um WebSocket, um terceiro para monitorar geolocalização.

Resumo

  • .task { } — modificador SwiftUI para operações async com auto-cancelamento quando a View desaparece.
  • Suporte async/await — dentro de .task, um contexto async completo está disponível sem necessidade de wrapper Task.
  • .task(id:) — reinicia a tarefa quando o identificador muda, substituindo .onChange manual.
  • Cancelamento cooperativo — use Task.checkCancellation() para verificar cancelamento dentro da tarefa.
  • Structured Concurrency — .task suporta TaskGroup e operações paralelas com cancelamento de tarefas filhas.
  • Substituto de .onAppear — para carregamento assíncrono de dados, use .task em vez do padrão .onAppear + Task + .onDisappear.
  • Tratamento de erros — todas as chamadas async dentro de .task devem estar envolvidas em do/catch para evitar crashes.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também