.task { } — was ist das, async-Modifikator und Datenladen in View

Autor: IT Sectr Veröffentlicht: 2026-06-26 Lesezeit: 9 Min.

.task { } ist ein in iOS 15 eingeführter SwiftUI-Modifikator, der eine asynchrone Operation startet, wenn eine View erscheint, und sie automatisch abbricht, wenn die View verschwindet. Im Gegensatz zu .onAppear, das synchronen Code ohne Abbruchmöglichkeit ausführt, arbeitet .task mit dem async/await-Kontext und berücksichtigt den Lebenszyklus der View: Wenn die View verschwindet, ruft SwiftUI cancel() auf dem erstellten Task auf. Dies verhindert Speicherlecks und die Ausführung von Operationen, nachdem die View nicht mehr aktualisiert werden muss. Laut Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024) ist .task die bevorzugte Methode zum Laden von Daten in SwiftUI, da es sicher mit Structured Concurrency arbeitet und die Lebensdauer der asynchronen Operation automatisch verwaltet.

Wichtige Punkte

  • .task { } — SwiftUI-Modifikator für asynchrones Datenladen beim Erscheinen einer View, verfügbar ab iOS 15.
  • Automatischer Abbruch — beim Verschwinden der View bricht SwiftUI den Task ab und verhindert Speicherlecks.
  • async/await-Kontext — innerhalb von .task sind async-Aufrufe ohne DispatchQueue oder Combine verfügbar.
  • .task(id:) — Variante mit Identifikator, die den Task bei Änderung des angegebenen Werts neu startet.
  • Structured Concurrency — .task unterstützt Structured Concurrency und TaskGroup für parallele Operationen.

Was ist .task { } in SwiftUI

.task { } ist ein View-Modifikator, der einen Task in einem async-Kontext erstellt, wenn die View auf dem Bildschirm erscheint. SwiftUI führt den übergebenen Closure im Hintergrundthread aus und lässt den Hauptthread für UI-Operationen frei. Wenn die View verschwindet, bricht SwiftUI den Task automatisch über den Structured-Concurrency-Mechanismus ab — dies stellt sicher, dass die asynchrone Operation nicht weiterläuft, nachdem ihr Ergebnis nicht mehr benötigt wird.

Laut Apple — Swift Programming Language (2025) verwendet .task das Konzept der Structured Concurrency, das in Swift 5.5 eingeführt wurde. Jeder .task erstellt einen Kind-Task innerhalb des Tasks der Eltern-View. Wenn der Eltern-Task abgebrochen wird (die View verschwindet), werden alle Kind-Tasks ebenfalls automatisch abgebrochen. Dies vereinfacht die Verwaltung des Lebenszyklus asynchroner Operationen im Vergleich zum manuellen Speichern von Referenzen auf DispatchWorkItem oder AnyCancellable radikal.

Im Gegensatz zum traditionellen Ansatz mit @State + manuellem Aufruf in .onAppear erfordert .task keine Speicherung einer Referenz auf den Task für den späteren Abbruch. SwiftUI erledigt dies automatisch, reduziert Boilerplate-Code und eliminiert das Risiko, einen Task-Abbruch zu vergessen.

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: Hauptunterschiede

Viele Entwickler sind es gewohnt, Daten in .onAppear zu laden, aber mit dem Aufkommen von async/await und .task ist dieser Ansatz veraltet. .onAppear führt Code synchron aus — für asynchrone Operationen innerhalb von .onAppear muss der Aufruf in Task { } gewrappt und manuell eine Referenz für den möglichen Abbruch vorgehalten werden. .task erledigt dies automatisch.

Eigenschaft.task { }.onAppear
Async-KontextIntegriertes async/awaitBenötigt Task { }-Wrapper
Auto-AbbruchJa, beim Verschwinden der ViewNein, muss manuell implementiert werden
Structured ConcurrencyUnterstütztNicht unterstützt
NeuausführungNur bei Änderung von idJedes Mal beim Erscheinen der View
Apple-EmpfehlungBevorzugte MethodeFür synchrone Operationen

.onAppear ist weiterhin nützlich für synchrone Operationen — zum Beispiel Logging oder initiale UI-Einrichtung. Aber für asynchrones Datenladen, Netzwerkanfragen, Datenbank- oder Dateisystemoperationen verwenden Sie .task. Es ist sicherer und architektonisch sauberer.

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

// ✅ Moderner Ansatz: .task verwaltet Abbruch
func body() { var body: some View { Text("") }
    .task { await loadData() }

.task(id:) — Neustart bei Datenänderung

Der .task(id:)-Modifikator akzeptiert einen zusätzlichen Parameter — einen Identifikator. Wenn sich der Wert des Identifikators ändert, bricht SwiftUI den aktuellen Task ab und startet einen neuen mit dem neuen Identifikator. Dies ist ideal für Bildschirme, bei denen Daten von einem ausgewählten Parameter abhängen — zum Beispiel eine Artikelliste nach Kategorie oder Produktdetails nach 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 {
            // Fehler behandeln
        }
    }
}

Wenn sich categoryId ändert, bricht SwiftUI die vorherige Anfrage ab und startet eine neue. Dies ist besonders wichtig bei schnellen Kategoriewechseln — alte Anfragen konkurrieren nicht mit neuen um die Statusaktualisierung. Ohne .task(id:) müssten Sie Änderungen manuell über .onChange verfolgen und den Task manuell verwalten.

Task-Abbruch und isCancelled-Prüfung

Obwohl .task den Task beim Verschwinden der View automatisch abbricht, muss die asynchrone Operation selbst kooperativ den Abbruch prüfen. Swift verwendet ein kooperatives Abbruchmodell — Task.cancel() stoppt die Ausführung nicht zwangsweise, sondern setzt lediglich das Flag isCancelled. Der Code innerhalb des Tasks sollte dieses Flag regelmäßig prüfen.

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

Task.checkCancellation() wirft CancellationError, wenn der Task abgebrochen wurde. Dies ist die einfachste Prüfmethode — sie funktioniert in jedem async-Kontext. Eine Alternative ist die manuelle Prüfung von Task.isCancelled vor aufwändigen Operationen. Bei URLSession werden Netzwerkanfragen automatisch abgebrochen, wenn der Task abgebrochen wird, da URLSession Structured Concurrency nativ unterstützt.

Praktische .task-Beispiele

.task eignet sich für viele Szenarien: vom einfachen JSON-Laden bis zu komplexen parallelen Operationen mit TaskGroup. Betrachten wir drei typische Anwendungsfälle.

Laden mit Fehlerbehandlung

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 fehlgeschlagen")
            }
        }
        .task {
            defer { isLoading = false }
            do {
                profile = await APIClient().fetchProfile()
            } catch {
                // Profil bleibt nil
            }
        }
    }
}

Paralleles Laden mit 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
            }
    }
}

Häufige .task-Fehler

Der häufigste Fehler ist das Mutieren von UI-Eigenschaften innerhalb von .task ohne Umschalten auf den Hauptthread. Obwohl SwiftUI Aktualisierungen automatisch an den Hauptthread zurückgibt, wenn @State in einem async-Kontext geändert wird, können direkte Manipulationen von UIKit-Elementen innerhalb von .task einen Absturz verursachen.

  • try/catch vergessen — .task behandelt Fehler nicht automatisch. Alle throwing-Funktionen müssen in do/catch eingewickelt werden, sonst stürzt die App ab.
  • Race Condition — wenn mehrere .task(id:) mit verschiedenen ids gestartet werden und denselben Status aktualisieren, können Wettlaufsituationen auftreten. Verwenden Sie separate Eigenschaften für verschiedene Datenquellen.
  • Lange synchrone Operationen — .task macht synchronen Code nicht asynchron. Wenn innerhalb von .task schwere synchrone Arbeit anfällt, wickeln Sie sie in Task.detached oder verschieben Sie sie in eine separate async-Methode.
  • CancellationError ignorieren — bei der Prüfung von Task.checkCancellation() sollte CancellationError nach oben propagiert, nicht unterdrückt werden. Das Unterdrücken von Abbruch kann zu Speicherlecks führen.
swift
// ❌ Fehler: keine Fehlerbehandlung
.task {
    let data = await fetchData() // stürzt bei Fehler ab!
    items = data
}

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

Häufig gestellte Fragen

Was ist der Unterschied zwischen .task und der Verwendung von Task { } in .onAppear?

.task bricht den Task automatisch ab, wenn die View verschwindet, und unterstützt Structured Concurrency. Task { } in .onAppear erfordert das manuelle Vorhalten einer Referenz auf den Task und den Aufruf von cancel() in .onDisappear. .task ist auch leichter zu lesen — es zeigt explizit an, dass das Datenladen Teil des View-Lebenszyklus ist.

Kann .task zum Abonnieren eines Publishers verwendet werden?

Ja, zum Abonnieren eines AsyncSequence oder AsyncStream verwenden Sie for await value in publisher.values innerhalb von .task. Dies funktioniert sowohl mit async/await als auch mit Combine über die Publisher.values-Erweiterung. Wenn die View verschwindet, endet die Iteration automatisch und das Abonnement wird gekündigt.

Wie arbeitet .task mit TabView — wird der Task beim Tab-Wechsel neu gestartet?

Ja, wenn die View innerhalb von TabView bei jedem Wechsel neu erstellt wird. Ab iOS 18 kann TabView Views im Speicher halten — in diesem Fall wird .task nicht neu gestartet. Verwenden Sie .task(id:) mit einem Tab-Identifikator, wenn Sie bei jedem Tab-Wechsel Daten neu laden müssen.

Was passiert, wenn eine View mit .task verschwindet, bevor die Anfrage abgeschlossen ist?

SwiftUI bricht den Task ab, wenn die View verschwindet. Wenn die URLSession-Anfrage innerhalb des Tasks war, wird auch sie abgebrochen. Wenn der Task keinen kooperativen Abbruch unterstützt (z. B. isCancelled nicht prüft), läuft er weiter, aber sein Ergebnis wird nicht auf den Status angewendet, da die View nicht mehr existiert.

Können mehrere .task-Modifikatoren auf einer einzigen View verwendet werden?

Ja, Sie können mehrere .task-Modifikatoren auf einer einzigen View hinzufügen. Jeder erstellt einen unabhängigen Task. Dies ist nützlich zur Trennung verschiedener Datenquellen: ein .task zum Laden eines Profils, ein zweiter zum Abonnieren eines WebSocket, ein dritter zur Überwachung des Standorts.

Zusammenfassung

  • .task { } — SwiftUI-Modifikator für asynchrone Operationen mit automatischem Abbruch beim Verschwinden der View.
  • async/await-Unterstützung — innerhalb von .task ist ein vollständiger async-Kontext ohne Task-Wrapper verfügbar.
  • .task(id:) — startet den Task bei Identifikatoränderung neu und ersetzt manuelles .onChange.
  • Kooperativer Abbruch — verwenden Sie Task.checkCancellation() zur Prüfung auf Abbruch innerhalb des Tasks.
  • Structured Concurrency — .task unterstützt TaskGroup und parallele Operationen mit Abbruch von Kind-Tasks.
  • Ersatz für .onAppear — für asynchrones Datenladen verwenden Sie .task statt des .onAppear + Task + .onDisappear-Musters.
  • Fehlerbehandlung — alle async-Aufrufe innerhalb von .task müssen zur Vermeidung von Abstürzen in do/catch eingewickelt werden.

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch