.task { } — egy módosító a SwiftUI-ban, amely az iOS 15-ben került bevezetésre, és elindít egy aszinkron műveletet a View megjelenésekor, valamint automatikusan megszakítja azt a View eltűnésekor. Ellentétben a .onAppear-rel, amely szinkron kódot hajt végre megszakítási lehetőség nélkül, a .task async/await kontextussal működik, és figyelembe veszi a View életciklusát: a View eltűnésekor a SwiftUI meghívja a cancel()-t a létrehozott Task-on. Ez megakadályozza a memóriaszivárgást és a műveletek végrehajtását, miután a View-t már nem kell frissíteni. Az Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024) szerint a .task az előnyben részesített módszer az adatbetöltésre a SwiftUI-ban, mivel biztonságosan együttműködik a Structured Concurrency-vel és automatikusan kezeli az aszinkron művelet élettartamát.
Főbb pontok
.task { } — egy View módosító, amely Task-ot hoz létre async kontextusban, amikor a View megjelenik a képernyőn. A SwiftUI elindítja a megadott closure-t a háttérben, miközben a fő szál szabad marad az UI műveletek számára. Amikor a View eltűnik, a SwiftUI automatikusan megszakítja a Task-ot a Structured Concurrency mechanizmusán keresztül — ez garantálja, hogy az aszinkron művelet nem folytatódik, miután az eredményére már nincs szükség.
Az Apple — Swift Programming Language (2025) szerint a .task a Structured Concurrency koncepciót használja, amely a Swift 5.5-ben került bevezetésre. Minden .task létrehoz egy gyermekfeladatot a szülő View feladatának keretében. Ha a szülő feladat megszakad (a View eltűnik), az összes gyermekfeladat is automatikusan megszakad. Ez radikálisan leegyszerűsíti az aszinkron műveletek életciklusának kezelését a DispatchWorkItem vagy AnyCancellable referenciák manuális tárolásához képest.
Ellentétben a hagyományos @State + manuális hívás .onAppear-ban megközelítéssel, a .task nem igényli a Task referenciájának tárolását későbbi megszakításhoz. A SwiftUI ezt automatikusan teszi, ami csökkenti a boilerplate kód mennyiségét és kiküszöböli a feladat megszakításának elfelejtéséből eredő kockázatot.
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
}
}
}
}
Sok fejlesztő megszokta, hogy adatokat tölt be a .onAppear-ben, de az async/await és a .task megjelenésével ez a megközelítés elavult. A .onAppear szinkron módon hajtja végre a kódot — az aszinkron műveletekhez a .onAppear-en belül a hívást Task { }-be kell csomagolni, és manuálisan tárolni egy referenciát a lehetséges megszakításhoz. A .task ezt automatikusan teszi.
| Jellemző | .task { } | .onAppear |
|---|---|---|
| Async kontextus | Beépített async/await | Task { } csomagolást igényel |
| Automatikus megszakítás | Igen, a View eltűnésekor | Nem, manuálisan kell megvalósítani |
| Structured Concurrency | Támogatja | Nem támogatja |
| Újraindítás | Csak az id változásakor | Minden megjelenéskor |
| Apple ajánlás | Előnyben részesített módszer | Szinkron műveletekhez |
A .onAppear továbbra is hasznos szinkron műveletekhez — például naplózáshoz vagy az UI kezdeti konfigurálásához. De aszinkron adatbetöltéshez, hálózati kérésekhez, adatbázis- vagy fájlrendszer-műveletekhez használd a .task-ot. Ez architektúrális szempontból biztonságosabb és tisztább.
// ❌ Régi megközelítés: Task a .onAppear-ben megszakítás nélkül
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
.onAppear {
loadTask = Task { await loadData() }
}
.onDisappear { loadTask?.cancel() }
// ✅ Modern megközelítés: .task kezeli a megszakítást
func body() { var body: some View { Text("") }
.task { await loadData() }
A .task(id:) módosító egy további paramétert — egy azonosítót — fogad. Amikor az azonosító értéke megváltozik, a SwiftUI megszakítja az aktuális feladatot, és újat indít az új azonosítóval. Ez ideális azokhoz a képernyőkhöz, ahol az adatok a kiválasztott paramétertől függenek — például cikklista kategória szerint vagy termékrészletek ID alapján.
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 {
// hiba kezelése
}
}
}
Amikor a categoryId megváltozik, a SwiftUI megszakítja az előző kérést, és újat indít. Ez különösen fontos a kategóriák gyors váltogatásakor — a régi kérések nem versenyeznek az újakkal az állapot frissítéséért. .task(id:) nélkül manuálisan kellene követni a változásokat .onChange segítségével, és manuálisan kezelni a Task-ot.
Bár a .task automatikusan megszakítja a feladatot a View eltűnésekor, magának az aszinkron műveletnek együttműködően kell ellenőriznie a megszakítást. A Swift együttműködő megszakítási modellt használ — a Task.cancel() nem állítja le erőszakkal a végrehajtást, csak beállítja az isCancelled flag-et. A feladatban lévő kódnak időszakonként ellenőriznie kell ezt a flag-et.
struct LoadingView: View {
@State var progress: Double = 0
var body: some View {
ProgressView(value: progress)
.task {
for i in 0..<100 {
// Megszakítás ellenőrzése
try Task.checkCancellation()
await Task.sleep(nanoseconds: 50_000_000)
progress = Double(i + 1) / 100.0
}
}
}
}
Task.checkCancellation() CancellationError-t dob, ha a feladat meg lett szakítva. Ez a legegyszerűbb ellenőrzési módszer — bármilyen async kontextusban működik. Alternatíva — a Task.isCancelled manuális ellenőrzése költséges műveletek előtt. Az URLSession hálózati kérések automatikusan megszakadnak a feladat megszakításakor, mivel az URLSession a kezdetektől támogatja a Structured Concurrency-t.
.task számos forgatókönyvhöz alkalmas: az egyszerű JSON betöltéstől a bonyolult, TaskGroup-pal végzett párhuzamos műveletekig. Nézzünk meg három tipikus használati példát.
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("Betöltés sikertelen")
}
}
.task {
defer { isLoading = false }
do {
profile = await APIClient().fetchProfile()
} catch {
// profil nil marad
}
}
}
}
struct DashboardView: View {
@State var stats: DashboardStats?
var body: some View {
Text("Irányítópult")
.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
}
}
}
A leggyakoribb hiba — UI tulajdonságok módosítása a .task-on belül anélkül, hogy átváltanánk a fő szálra. Bár a SwiftUI automatikusan visszaküldi a frissítéseket a fő szálra a @State változásakor async kontextusban, az UIKit elemek közvetlen manipulációja a .task-on belül crash-t okozhat.
// ❌ Hiba: nincs hibakezelés
.task {
let data = await fetchData() // hibánál összeomlik!
items = data
}
// ✅ Helyes: do/catch
.task {
do {
items = await fetchData()
} catch {
errorMessage = error.localizedDescription
}
}
Gyakran Ismételt Kérdések
A .task automatikusan megszakítja a feladatot a View eltűnésekor, és támogatja a Structured Concurrency-t. A Task { } a .onAppear-ben megköveteli a feladatra való referenciák manuális tárolását és a cancel() meghívását a .onDisappear-ben. A .task könnyebben olvasható is — egyértelműen jelzi, hogy az adatbetöltés a View életciklusának része.
Igen, az AsyncSequence vagy AsyncStream feliratkozáshoz használd a for await value in publisher.values kifejezést a .task-on belül. Ez működik async/await-tel és Combine-nal is a Publisher.values kiterjesztésen keresztül. A View eltűnésekor az iteráció automatikusan véget ér, és a feliratkozás megszakad.
Igen, ha a TabView-n belüli View minden váltáskor újra létrejön. iOS 18-tól kezdve a TabView tárolhatja a View-t a memóriában — ebben az esetben a .task nem indul újra. Használj .task(id:)-t a lap azonosítójával, ha minden váltáskor újra kell tölteni az adatokat.
A SwiftUI megszakítja a feladatot a View eltűnésekor. Ha az URLSession kérés a feladaton belül volt, az is megszakad. Ha a feladat nem támogatja az együttműködő megszakítást (pl. nem ellenőrzi az isCancelled-t), a végrehajtás folytatódik, de az eredmény nem kerül alkalmazásra az állapotban, mivel a View már nem létezik.
Igen, több .task módosító is hozzáadható egy View-hoz. Mindegyik független feladatot hoz létre. Ez kényelmes a különböző adatforrások szétválasztására: az egyik .task a profil betöltésére, a második a WebSocket feliratkozásra, a harmadik a geolokáció figyelésére.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is