.task { } — ay isang modifier sa SwiftUI, na ipinakilala sa iOS 15, na naglulunsad ng asynchronous na operasyon kapag lumitaw ang View at awtomatikong kinakansela ito kapag nawala. Hindi tulad ng .onAppear, na nagsasagawa ng synchronous na code nang walang kakayahang kanselahin, ang .task ay gumagana sa async/await na konteksto at isinasaalang-alang ang lifecycle ng View: kapag nawala ang View, tinatawag ng SwiftUI ang cancel() sa nilikhang Task. Pinipigilan nito ang mga leak ng memorya at pagpapatakbo ng mga operasyon pagkatapos na hindi na kailangang i-update ang View. Ayon sa Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024), ang .task ay ang ginustong paraan ng pag-load ng data sa SwiftUI, dahil ligtas itong gumagana sa Structured Concurrency at awtomatikong namamahala sa buhay ng asynchronous na operasyon.
Mga Pangunahing Punto
.task { } — ay isang View modifier na lumilikha ng Task sa async na konteksto kapag lumitaw ang View sa screen. Inilulunsad ng SwiftUI ang ibinigay na closure sa background, habang ang pangunahing thread ay nananatiling libre para sa mga UI operation. Kapag nawala ang View, awtomatikong kinakansela ng SwiftUI ang Task sa pamamagitan ng Structured Concurrency mechanism — ginagarantiyahan nito na ang asynchronous na operasyon ay hindi magpapatuloy pagkatapos na ang resulta nito ay hindi na kailangan ng sinuman.
Ayon sa Apple — Swift Programming Language (2025), ginagamit ng .task ang konsepto ng Structured Concurrency, na ipinakilala sa Swift 5.5. Ang bawat .task ay lumilikha ng child task sa loob ng parent task ng View. Kung ang parent task ay nakansela (nawala ang View), lahat ng child task ay awtomatikong nakansela rin. Ito ay radikal na nagpapasimple sa pamamahala ng lifecycle ng mga asynchronous na operasyon kumpara sa manu-manong pag-iimbak ng mga referensya sa DispatchWorkItem o AnyCancellable.
Hindi tulad ng tradisyonal na diskarte na may @State + manu-manong tawag sa .onAppear, ang .task ay hindi nangangailangan ng pag-iimbak ng referensya sa Task para sa susunod na pagkansela. Ginagawa ito ng SwiftUI nang awtomatiko, na nagbabawas sa dami ng boilerplate code at nag-aalis ng panganib na makalimutang kanselahin ang gawain.
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
}
}
}
}
Maraming developer ang sanay na mag-load ng data sa .onAppear, ngunit sa pagdating ng async/await at .task, ang diskarteng ito ay luma na. Ang .onAppear ay nagsasagawa ng code nang synchronously — para sa mga asynchronous na operasyon sa loob ng .onAppear, kailangan mong balutin ang tawag sa Task { } at manu-manong mag-imbak ng referensya dito para sa posibleng pagkansela. Ginagawa ito ng .task nang awtomatiko.
| Katangian | .task { } | .onAppear |
|---|---|---|
| Async na konteksto | Nakapaloob na async/await | Nangangailangan ng Task { } balot |
| Awtomatikong pagkansela | Oo, kapag nawala ang View | Hindi, kailangan manual na ipatupad |
| Structured Concurrency | Sinusuportahan | Hindi sinusuportahan |
| Pag-restart | Kapag nagbago lang ang id | Tuwing lumilitaw |
| Rekomendasyon ng Apple | Ginustong paraan | Para sa synchronous na operasyon |
.onAppear ay kapaki-pakinabang pa rin para sa mga synchronous na operasyon — halimbawa, pag-log o paunang configuration ng UI. Ngunit para sa asynchronous na pag-load ng data, mga network request, pagtatrabaho sa database o file system, gamitin ang .task. Ito ay mas ligtas at mas malinis mula sa pananaw ng arkitektura.
// ❌ Lumang diskarte: Task sa .onAppear nang walang pagkansela
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
.onAppear {
loadTask = Task { await loadData() }
}
.onDisappear { loadTask?.cancel() }
// ✅ Makabagong diskarte: .task ang namamahala sa pagkansela
func body() { var body: some View { Text("") }
.task { await loadData() }
Ang modifier na .task(id:) ay tumatanggap ng karagdagang parameter — isang identifier. Kapag nagbago ang halaga ng identifier, kinakansela ng SwiftUI ang kasalukuyang gawain at naglulunsad ng bago na may bagong identifier. Ito ay perpekto para sa mga screen kung saan ang data ay nakadepende sa napiling parameter — halimbawa, listahan ng mga artikulo ayon sa kategorya o mga detalye ng produkto ayon sa 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 {
// hawakan ang error
}
}
}
Kapag nagbago ang categoryId, kinakansela ng SwiftUI ang nakaraang request at naglulunsad ng bago. Ito ay lalong mahalaga sa mabilis na pagpalit ng mga kategorya — ang mga lumang request ay hindi makikipagkumpitensya sa mga bago para sa pag-update ng state. Kung walang .task(id:) kailangan mong manu-manong subaybayan ang mga pagbabago sa pamamagitan ng .onChange at manu-manong pamahalaan ang Task.
Kahit na ang .task ay awtomatikong kinakansela ang gawain kapag nawala ang View, ang asynchronous na operasyon mismo ay dapat na kooperatibong suriin ang pagkansela. Gumagamit ang Swift ng kooperatibong modelo ng pagkansela — ang Task.cancel() ay hindi puwersahang humihinto sa pagpapatakbo, ngunit nagtatakda lamang ng flag na isCancelled. Ang code sa loob ng gawain ay dapat na pana-panahong suriin ang flag na ito.
struct LoadingView: View {
@State var progress: Double = 0
var body: some View {
ProgressView(value: progress)
.task {
for i in 0..<100 {
// Suriin ang pagkansela
try Task.checkCancellation()
await Task.sleep(nanoseconds: 50_000_000)
progress = Double(i + 1) / 100.0
}
}
}
}
Task.checkCancellation() ay nagtatapon ng CancellationError kung ang gawain ay nakansela. Ito ang pinakasimpleng paraan ng pagsusuri — gumagana sa anumang async na konteksto. Alternatibo — manu-manong pagsusuri ng Task.isCancelled bago ang mga mamahaling operasyon. Para sa URLSession, ang mga network request ay awtomatikong nakansela kapag nakansela ang gawain, dahil sinusuportahan ng URLSession ang Structured Concurrency mula sa simula.
.task ay angkop para sa maraming senaryo: mula sa simpleng pag-load ng JSON hanggang sa kumplikadong parallel na operasyon gamit ang TaskGroup. Tingnan natin ang tatlong tipikal na halimbawa ng paggamit.
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("Nabigong mag-load")
}
}
.task {
defer { isLoading = false }
do {
profile = await APIClient().fetchProfile()
} catch {
// profile ay nananatiling nil
}
}
}
}
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
}
}
}
Ang pinakakaraniwang pagkakamali — pag-mutate ng mga UI property sa loob ng .task nang hindi lumilipat sa pangunahing thread. Kahit na awtomatikong ibinabalik ng SwiftUI ang mga update sa pangunahing thread kapag nagbago ang @State sa async na konteksto, ang direktang pagmamanipula ng mga elemento ng UIKit sa loob ng .task ay maaaring magdulot ng crash.
// ❌ Error: walang paghawak ng error
.task {
let data = await fetchData() // bumagsak sa error!
items = data
}
// ✅ Tama: do/catch
.task {
do {
items = await fetchData()
} catch {
errorMessage = error.localizedDescription
}
}
Mga Madalas Itanong
.task ay awtomatikong kinakansela ang gawain kapag nawala ang View at sinusuportahan ang Structured Concurrency. Ang Task { } sa .onAppear ay nangangailangan ng manu-manong pag-iimbak ng referensya sa gawain at pagtawag ng cancel() sa .onDisappear. Ang .task ay mas madali ring basahin — malinaw nitong ipinapahiwatig na ang pag-load ng data ay bahagi ng lifecycle ng View.
Oo, para mag-subscribe sa AsyncSequence o AsyncStream, gamitin ang for await value in publisher.values sa loob ng .task. Gumagana ito pareho sa async/await at sa Combine sa pamamagitan ng extension na Publisher.values. Kapag nawala ang View, awtomatikong magtatapos ang iteration at makakansela ang subscription.
Oo, kung ang View sa loob ng TabView ay muling nililikha sa bawat paglipat ng tab. Mula iOS 18, ang TabView ay maaaring mag-imbak ng View sa memorya — sa kasong ito, hindi nagre-restart ang .task. Gamitin ang .task(id:) na may identifier ng tab kung kailangan mong mag-reload ng data sa bawat paglipat ng tab.
Kinakansela ng SwiftUI ang gawain kapag nawala ang View. Kung ang URLSession request ay nasa loob ng gawain, ito ay makakansela rin. Kung ang gawain ay hindi sumusuporta sa kooperatibong pagkansela (halimbawa, hindi sinusuri ang isCancelled), magpapatuloy ito, ngunit ang resulta nito ay hindi ilalapat sa state dahil wala na ang View.
Oo, maaari kang magdagdag ng maramihang .task modifier sa isang View. Bawat isa ay lumilikha ng independiyenteng gawain. Ito ay maginhawa para sa paghihiwalay ng iba't ibang source ng data: isang .task para sa pag-load ng profile, pangalawa para sa pag-subscribe sa WebSocket, pangatlo para sa pag-monitor ng geolocation.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din