.task { } — wat is het, async modifier en gegevens laden in View

Auteur: IT Sectr Gepubliceerd: 2026-06-26 Leestijd: 9 min

.task { } — is een modifier in SwiftUI, geïntroduceerd in iOS 15, die een asynchrone bewerking start wanneer een View verschijnt en deze automatisch annuleert wanneer deze verdwijnt. In tegenstelling tot .onAppear, die synchrone code uitvoert zonder annuleringsmogelijkheid, werkt .task met een async/await context en houdt rekening met de levenscyclus van de View: wanneer de View verdwijnt, roept SwiftUI cancel() aan op de aangemaakte Task. Dit voorkomt geheugenlekken en het uitvoeren van bewerkingen nadat de View niet meer hoeft te worden bijgewerkt. Volgens Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024) is .task de voorkeursmethode voor het laden van gegevens in SwiftUI, omdat het veilig samenwerkt met Structured Concurrency en automatisch de levensduur van de asynchrone bewerking beheert.

Belangrijkste punten

  • .task { } — SwiftUI modifier voor asynchroon laden van gegevens bij het verschijnen van een View, beschikbaar vanaf iOS 15.
  • Automatisch annuleren — wanneer de View verdwijnt, annuleert SwiftUI de Task, waardoor geheugenlekken worden voorkomen.
  • async/await context — binnen .task zijn async-aanroepen beschikbaar zonder DispatchQueue of Combine.
  • .task(id:) — variant met identifier herstart de taak bij wijziging van de opgegeven waarde.
  • Structured Concurrency — .task ondersteunt Structured Concurrency en TaskGroup voor parallelle bewerkingen.

Wat is .task { } in SwiftUI

.task { } — is een View modifier die een Task aanmaakt in async context wanneer de View op het scherm verschijnt. SwiftUI start de doorgegeven closure op de achtergrond, terwijl de hoofdthread vrij blijft voor UI-bewerkingen. Wanneer de View verdwijnt, annuleert SwiftUI automatisch de Task via het Structured Concurrency mechanisme — dit garandeert dat de asynchrone bewerking niet doorgaat nadat het resultaat ervan niet meer nodig is.

Volgens Apple — Swift Programming Language (2025) gebruikt .task het Structured Concurrency concept, geïntroduceerd in Swift 5.5. Elke .task maakt een subtask aan binnen de taak van de bovenliggende View. Als de bovenliggende taak wordt geannuleerd (View verdwijnt), worden alle subtaken ook automatisch geannuleerd. Dit vereenvoudigt het beheer van de levenscyclus van asynchrone bewerkingen radicaal in vergelijking met het handmatig bijhouden van verwijzingen naar DispatchWorkItem of AnyCancellable.

In tegenstelling tot de traditionele aanpak met @State + handmatige aanroep in .onAppear, vereist .task niet dat je een verwijzing naar de Task bewaart voor latere annulering. SwiftUI doet dit automatisch, wat de hoeveelheid boilerplate code vermindert en het risico elimineert om te vergeten een taak te annuleren.

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: belangrijkste verschillen

Veel ontwikkelaars zijn gewend om gegevens te laden in .onAppear, maar met de komst van async/await en .task is deze aanpak verouderd. .onAppear voert code synchroon uit — voor asynchrone bewerkingen binnen .onAppear moet je de aanroep in Task { } wrappen en handmatig een verwijzing ernaar bewaren voor mogelijke annulering. .task doet dit automatisch.

Kenmerk.task { }.onAppear
Async contextIngebouwde async/awaitVereist Task { } wrapper
Auto-annulerenJa, bij verdwijnen van ViewNee, moet handmatig worden geïmplementeerd
Structured ConcurrencyOndersteuntOndersteunt niet
HerstartenAlleen bij wijziging van idElke keer bij verschijnen
Aanbeveling AppleVoorkeursmethodeVoor synchrone bewerkingen

.onAppear is nog steeds nuttig voor synchrone bewerkingen — bijvoorbeeld loggen of initiële UI-configuratie. Maar voor asynchroon laden van gegevens, netwerkverzoeken, werken met databases of bestandssystemen, gebruik .task. Dit is veiliger en schoner vanuit architectonisch oogpunt.

swift
// ❌ Legacy aanpak: Task in .onAppear zonder annulering
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
    .onAppear {
        loadTask = Task { await loadData() }
    }
    .onDisappear { loadTask?.cancel() }

// ✅ Moderne aanpak: .task beheert annulering
func body() { var body: some View { Text("") }
    .task { await loadData() }

.task(id:) — herstart bij wijziging van gegevens

De modifier .task(id:) accepteert een extra parameter — een identifier. Wanneer de waarde van de identifier verandert, annuleert SwiftUI de huidige taak en start een nieuwe met de nieuwe identifier. Dit is ideaal voor schermen waar gegevens afhankelijk zijn van een geselecteerde parameter — bijvoorbeeld een lijst met artikelen per categorie of productdetails op 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 {
            // fout afhandelen
        }
    }
}

Wanneer categoryId verandert, annuleert SwiftUI het vorige verzoek en start een nieuw. Dit is vooral belangrijk bij snel schakelen tussen categorieën — oude verzoeken zullen niet concurreren met nieuwe voor het bijwerken van de status. Zonder .task(id:) zou je wijzigingen handmatig moeten volgen via .onChange en de Task handmatig moeten beheren.

Taken annuleren en isCancelled controleren

Hoewel .task de taak automatisch annuleert wanneer de View verdwijnt, moet de asynchrone bewerking zelf coöperatief annulering controleren. Swift gebruikt een coöperatief annuleringsmodel — Task.cancel() stopt de uitvoering niet geforceerd, maar stelt alleen de isCancelled-vlag in. Code binnen de taak moet deze vlag periodiek controleren.

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

Task.checkCancellation() gooit CancellationError als de taak is geannuleerd. Dit is de eenvoudigste controlemethode — het werkt in elke async context. Alternatief — handmatig controleren van Task.isCancelled voor dure bewerkingen. Voor URLSession worden netwerkverzoeken automatisch geannuleerd bij annulering van de taak, omdat URLSession Structured Concurrency vanaf het begin ondersteunt.

Praktische voorbeelden van .task

.task is geschikt voor vele scenario's: van eenvoudig JSON laden tot complexe parallelle bewerkingen met TaskGroup. Laten we drie typische gebruiksvoorbeelden bekijken.

Laden met foutafhandeling

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("Laden mislukt")
            }
        }
        .task {
            defer { isLoading = false }
            do {
                profile = await APIClient().fetchProfile()
            } catch {
                // profiel blijft nil
            }
        }
    }
}

Parallel laden met TaskGroup

swift
struct DashboardView: View {
    @State var stats: DashboardStats?
    
    var body: some View {
        Text("Dashboard")
            .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
            }
    }
}

Veelvoorkomende fouten met .task

De meest voorkomende fout — het muteren van UI-eigenschappen binnen .task zonder naar de hoofdthread te schakelen. Hoewel SwiftUI automatisch updates naar de hoofdthread terugstuurt bij wijziging van @State in async context, kunnen directe manipulaties van UIKit-elementen binnen .task een crash veroorzaken.

  • try/catch vergeten — .task handelt fouten niet automatisch af. Alle functies die fouten gooien, moeten binnen do/catch worden gewrapped, anders crasht de applicatie.
  • Race condition — als meerdere .task(id:) met verschillende id's worden gestart en dezelfde status bijwerken, zijn racecondities mogelijk. Gebruik aparte eigenschappen voor verschillende gegevensbronnen.
  • Lange synchrone bewerkingen — .task maakt synchrone code niet asynchroon. Als er binnen .task zwaar synchroon werk is, wrap het dan in Task.detached of verplaats het naar een aparte async-methode.
  • Negeren van CancellationError — bij controle van Task.checkCancellation() moet de CancellationError fout worden doorgegeven, niet worden onderdrukt. Het onderdrukken van annulering kan leiden tot geheugenlekken.
swift
// ❌ Fout: geen foutafhandeling
.task {
    let data = await fetchData() // crasht bij fout!
    items = data
}

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

Veelgestelde vragen

Wat is het verschil tussen .task en het gebruik van Task { } in .onAppear?

.task annuleert de taak automatisch wanneer de View verdwijnt en ondersteunt Structured Concurrency. Task { } in .onAppear vereist het handmatig bewaren van een verwijzing naar de taak en het aanroepen van cancel() in .onDisappear. .task is ook makkelijker te lezen — het geeft expliciet aan dat het laden van gegevens deel uitmaakt van de levenscyclus van de View.

Kan .task worden gebruikt om te abonneren op een publisher?

Ja, om je te abonneren op AsyncSequence of AsyncStream gebruik je for await value in publisher.values binnen .task. Dit werkt zowel met async/await als met Combine via de Publisher.values extensie. Wanneer de View verdwijnt, stopt de iteratie automatisch en wordt het abonnement geannuleerd.

Hoe werkt .task met TabView — wordt de taak herstart bij het schakelen tussen tabbladen?

Ja, als de View binnen TabView bij elke schakeling opnieuw wordt aangemaakt. Vanaf iOS 18 kan TabView de View in het geheugen bewaren — in dat geval wordt .task niet opnieuw gestart. Gebruik .task(id:) met de tabblad-identifier als je gegevens opnieuw moet laden bij elke schakeling.

Wat gebeurt er als de View met .task verdwijnt voordat het verzoek is voltooid?

SwiftUI annuleert de taak wanneer de View verdwijnt. Als het URLSession-verzoek binnen de taak was, wordt het ook geannuleerd. Als de taak geen coöperatieve annulering ondersteunt (bijvoorbeeld geen isCancelled controleert), gaat de uitvoering door, maar het resultaat wordt niet toegepast op de status omdat de View niet meer bestaat.

Kunnen meerdere .task op één View worden gebruikt?

Ja, je kunt meerdere .task modifiers aan één View toevoegen. Elk maakt een onafhankelijke taak aan. Dit is handig voor het scheiden van verschillende gegevensbronnen: één .task voor het laden van het profiel, een tweede voor abonneren op WebSocket, een derde voor het monitoren van geolocatie.

Samenvatting

  • .task { } — SwiftUI modifier voor asynchrone bewerkingen met automatisch annuleren bij verdwijnen van View.
  • async/await ondersteuning — binnen .task is een volledige async context beschikbaar zonder Task wrapper.
  • .task(id:) — herstart de taak bij wijziging van de identifier, vervangt handmatige .onChange.
  • Coöperatief annuleren — gebruik Task.checkCancellation() om annulering binnen de taak te controleren.
  • Structured Concurrency — .task ondersteunt TaskGroup en parallelle bewerkingen met annulering van subtaken.
  • Vervanging van .onAppear — voor asynchroon laden van gegevens gebruik .task in plaats van de combinatie .onAppear + Task + .onDisappear.
  • Foutafhandeling — alle async-aanroepen binnen .task moeten in do/catch worden gewrapped om crashes te voorkomen.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook