.task { } — är en modifierare i SwiftUI, introducerad i iOS 15, som startar en asynkron operation när en View visas och automatiskt avbryter den när den försvinner. Till skillnad från .onAppear, som utför synkron kod utan möjlighet till avbokning, arbetar .task med async/await-kontext och tar hänsyn till View:ns livscykel: när View försvinner anropar SwiftUI cancel() på den skapade Task. Detta förhindrar minnesläckor och utförande av operationer efter att View inte längre behöver uppdateras. Enligt Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024) är .task den föredragna metoden för att ladda data i SwiftUI, eftersom den fungerar säkert med Structured Concurrency och automatiskt hanterar den asynkrona operationens livstid.
Huvudpunkter
.task { } — är en View-modifierare som skapar en Task i async-kontext när View visas på skärmen. SwiftUI startar den angivna closure i bakgrunden, medan huvudtråden förblir fri för UI-operationer. När View försvinner avbryter SwiftUI automatiskt Task via Structured Concurrency-mekanismen — detta garanterar att den asynkrona operationen inte fortsätter efter att dess resultat inte längre behövs av någon.
Enligt Apple — Swift Programming Language (2025) använder .task konceptet Structured Concurrency, som introducerades i Swift 5.5. Varje .task skapar en underordnad uppgift inom ramen för den överordnade View:ns uppgift. Om den överordnade uppgiften avbryts (View försvinner) avbryts även alla underordnade uppgifter automatiskt. Detta förenklar hanteringen av livscykeln för asynkrona operationer radikalt jämfört med manuell lagring av referenser till DispatchWorkItem eller AnyCancellable.
Till skillnad från det traditionella tillvägagångssättet med @State + manuellt anrop i .onAppear kräver .task inte att man lagrar en referens till Task för senare avbokning. SwiftUI gör detta automatiskt, vilket minskar mängden boilerplate-kod och eliminerar risken att glömma att avbryta uppgiften.
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
}
}
}
}
Många utvecklare är vana vid att ladda data i .onAppear, men med tillkomsten av async/await och .task är detta tillvägagångssätt föråldrat. .onAppear utför kod synkront — för asynkrona operationer inuti .onAppear måste du slå in anropet i Task { } och manuellt lagra en referens till det för eventuell avbokning. .task gör detta automatiskt.
| Egenskap | .task { } | .onAppear |
|---|---|---|
| Async-kontext | Inbyggd async/await | Kräver Task { }-omslag |
| Autoavbokning | Ja, när View försvinner | Nej, måste implementeras manuellt |
| Structured Concurrency | Stöder | Stöder inte |
| Omstart | Endast när id ändras | Varje gång View visas |
| Apple-rekommendation | Föredragen metod | För synkrona operationer |
.onAppear är fortfarande användbart för synkrona operationer — till exempel loggning eller initial UI-konfiguration. Men för asynkron dataladdning, nätverksförfrågningar, arbete med databas eller filsystem, använd .task. Detta är säkrare och renare ur arkitektursynpunkt.
// ❌ Gammalt tillvägagångssätt: Task i .onAppear utan avbokning
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
.onAppear {
loadTask = Task { await loadData() }
}
.onDisappear { loadTask?.cancel() }
// ✅ Modernt tillvägagångssätt: .task hanterar avbokning
func body() { var body: some View { Text("") }
.task { await loadData() }
Modifieraren .task(id:) accepterar en extra parameter — en identifierare. När identifierarens värde ändras avbryter SwiftUI den aktuella uppgiften och startar en ny med den nya identifieraren. Detta är idealiskt för skärmar där data beror på en vald parameter — till exempel en lista med artiklar efter kategori eller produktdetaljer efter ID.
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 {
// hantera fel
}
}
}
När categoryId ändras avbryter SwiftUI den tidigare förfrågan och startar en ny. Detta är särskilt viktigt vid snabb växling av kategorier — gamla förfrågningar kommer inte att konkurrera med nya om att uppdatera tillståndet. Utan .task(id:) skulle du manuellt behöva spåra ändringar via .onChange och manuellt hantera Task.
Även om .task automatiskt avbryter uppgiften när View försvinner, måste den asynkrona operationen själv kooperativt kontrollera avbokning. Swift använder en kooperativ modell för avbokning — Task.cancel() stoppar inte exekveringen med våld, utan ställer bara in flaggan isCancelled. Kod inuti uppgiften bör periodiskt kontrollera denna flagga.
struct LoadingView: View {
@State var progress: Double = 0
var body: some View {
ProgressView(value: progress)
.task {
for i in 0..<100 {
// Kontrollera avbokning
try Task.checkCancellation()
await Task.sleep(nanoseconds: 50_000_000)
progress = Double(i + 1) / 100.0
}
}
}
}
Task.checkCancellation() kastar CancellationError om uppgiften har avbrutits. Detta är det enklaste sättet att kontrollera — fungerar i alla async-kontexter. Alternativ — manuell kontroll av Task.isCancelled före dyra operationer. För URLSession avbryts nätverksförfrågningar automatiskt när uppgiften avbryts, eftersom URLSession stöder Structured Concurrency från början.
.task passar för många scenarier: från enkel JSON-laddning till komplexa parallella operationer med TaskGroup. Låt oss titta på tre typiska användningsexempel.
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("Misslyckades att ladda")
}
}
.task {
defer { isLoading = false }
do {
profile = await APIClient().fetchProfile()
} catch {
// profil förblir nil
}
}
}
}
struct DashboardView: View {
@State var stats: DashboardStats?
var body: some View {
Text("Instrumentpanel")
.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
}
}
}
Det vanligaste misstaget — mutering av UI-egenskaper inuti .task utan att växla till huvudtråden. Även om SwiftUI automatiskt returnerar uppdateringar till huvudtråden när @State ändras i async-kontext, kan direkt manipulation av UIKit-element inuti .task orsaka en krasch.
// ❌ Fel: ingen felhantering
.task {
let data = await fetchData() // kraschar vid fel!
items = data
}
// ✅ Korrekt: do/catch
.task {
do {
items = await fetchData()
} catch {
errorMessage = error.localizedDescription
}
}
Vanliga frågor
.task avbryter automatiskt uppgiften när View försvinner och stöder Structured Concurrency. Task { } i .onAppear kräver manuell lagring av en referens till uppgiften och anrop av cancel() i .onDisappear. .task är också lättare att läsa — den anger explicit att dataladdning är en del av View:ns livscykel.
Ja, för att prenumerera på AsyncSequence eller AsyncStream, använd for await value in publisher.values inuti .task. Detta fungerar både med async/await och med Combine via Publisher.values-tillägget. När View försvinner avslutas iterationen automatiskt och prenumerationen avbryts.
Ja, om View inuti TabView återskapas vid varje växling. Från iOS 18 kan TabView behålla View i minnet — i så fall startas .task inte om. Använd .task(id:) med flikens identifierare om du behöver ladda om data vid varje växling.
SwiftUI avbryter uppgiften när View försvinner. Om URLSession-förfrågan var inuti uppgiften kommer den också att avbrytas. Om uppgiften inte stöder kooperativ avbokning (t.ex. kontrollerar inte isCancelled) kommer den att fortsätta, men dess resultat kommer inte att tillämpas på tillståndet eftersom View inte längre finns.
Ja, du kan lägga till flera .task-modifierare på en View. Varje skapar en oberoende uppgift. Detta är praktiskt för att separera olika datakällor: en .task för att ladda profil, en andra för att prenumerera på WebSocket, en tredje för att övervaka geolokalisering.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också