.task { }, iOS 15'te tanıtılan ve bir View göründüğünde asenkron bir işlem başlatan ve View kaybolduğunda otomatik olarak iptal eden bir SwiftUI değiştiricisidir. İptal imkanı olmadan senkron kod çalıştıran .onAppear'ın aksine, .task async/await bağlamıyla çalışır ve View yaşam döngüsünü dikkate alır: View kaybolduğunda, SwiftUI oluşturulan Task üzerinde cancel() çağrısı yapar. Bu, bellek sızıntılarını ve View'ın artık güncellenmesi gerekmediği halde işlemlerin yürütülmesini önler. Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024)'e göre, .task SwiftUI'de veri yüklemek için tercih edilen yöntemdir çünkü Structured Concurrency ile güvenli bir şekilde çalışır ve asenkron işlemin ömrünü otomatik olarak yönetir.
Önemli Noktalar
.task { }, View ekranda göründüğünde async bağlamında bir Task oluşturan bir View değiştiricisidir. SwiftUI sağlanan closure'ı arka plan iş parçacığında çalıştırır ve ana iş parçacığını UI işlemleri için serbest bırakır. View kaybolduğunda, SwiftUI Structured Concurrency mekanizması aracılığıyla Task'ı otomatik olarak iptal eder — bu, asenkron işlemin sonucu artık gerekli olmadıktan sonra da yürütülmeye devam etmemesini garanti eder.
Apple — Swift Programming Language (2025)'e göre, .task Swift 5.5'te tanıtılan Structured Concurrency kavramını kullanır. Her .task, üst View'ın görevi kapsamında bir alt görev oluşturur. Üst görev iptal edilirse (View kaybolursa), tüm alt görevler de otomatik olarak iptal edilir. Bu, DispatchWorkItem veya AnyCancellable referanslarını manuel olarak saklamaya kıyasla asenkron işlemlerin yaşam döngüsü yönetimini kökten basitleştirir.
@State + .onAppear'da manuel çağrı içeren geleneksel yaklaşımın aksine, .task daha sonra iptal için Task'a bir referans saklamayı gerektirmez. SwiftUI bunu otomatik olarak yapar, tekrarlayan kodu azaltır ve bir görevi iptal etmeyi unutma riskini ortadan kaldırır.
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
}
}
}
}
Birçok geliştirici .onAppear içinde veri yüklemeye alışkındır, ancak async/await ve .task'in gelişiyle bu yaklaşım güncelliğini yitirmiştir. .onAppear kodu senkron olarak çalıştırır — .onAppear içindeki asenkron işlemler için, çağrıyı Task { } içine sarmak ve olası iptal için manuel olarak bir referans tutmak gerekir. .task bunu otomatik olarak yapar.
| Özellik | .task { } | .onAppear |
|---|---|---|
| Async bağlamı | Yerleşik async/await | Task { } sarmalayıcısı gerekli |
| Otomatik iptal | Evet, View kaybolduğunda | Hayır, manuel olarak uygulanmalı |
| Structured Concurrency | Destekler | Desteklemez |
| Yeniden çalıştırma | Sadece id değiştiğinde | View her göründüğünde |
| Apple tavsiyesi | Tercih edilen yöntem | Senkron işlemler için |
.onAppear senkron işlemler için hala kullanışlıdır — örneğin, günlükleme veya başlangıç UI kurulumu. Ancak asenkron veri yükleme, ağ istekleri, veritabanı veya dosya sistemi işlemleri için .task kullanın. Mimari açıdan daha güvenli ve temizdir.
// ❌ Eski yaklaşım: .onAppear'da iptalsiz Task
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
.onAppear {
loadTask = Task { await loadData() }
}
.onDisappear { loadTask?.cancel() }
// ✅ Modern yaklaşım: .task iptali yönetir
func body() { var body: some View { Text("") }
.task { await loadData() }
.task(id:) değiştiricisi ek bir parametre — bir tanımlayıcı kabul eder. Tanımlayıcının değeri değiştiğinde, SwiftUI mevcut görevi iptal eder ve yeni tanımlayıcıyla yeni bir görev başlatır. Bu, verilerin seçilen bir parametreye bağlı olduğu ekranlar için idealdir — örneğin, kategoriye göre makale listesi veya ID'ye göre ürün detayları.
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 {
// hatayı işle
}
}
}
categoryId değiştiğinde, SwiftUI önceki isteği iptal eder ve yenisini başlatır. Bu, özellikle hızlı kategori geçişlerinde önemlidir — eski istekler durum güncellemesi için yenileriyle rekabet etmez. .task(id:) olmadan, değişiklikleri .onChange aracılığıyla manuel olarak izlemeniz ve Task'ı manuel olarak yönetmeniz gerekirdi.
.task View kaybolduğunda görevi otomatik olarak iptal etse de, asenkron işlemin kendisi iptali işbirlikçi bir şekilde kontrol etmelidir. Swift işbirlikçi bir iptal modeli kullanır — Task.cancel() yürütmeyi zorla durdurmaz, yalnızca isCancelled bayrağını ayarlar. Görev içindeki kod, bu bayrağı periyodik olarak kontrol etmelidir.
struct LoadingView: View {
@State var progress: Double = 0
var body: some View {
ProgressView(value: progress)
.task {
for i in 0..<100 {
// İptali kontrol et
try Task.checkCancellation()
await Task.sleep(nanoseconds: 50_000_000)
progress = Double(i + 1) / 100.0
}
}
}
}
Task.checkCancellation(), görev iptal edildiyse CancellationError fırlatır. Bu, kontrol etmenin en basit yoludur — herhangi bir async bağlamında çalışır. Bir alternatif, pahalı işlemlerden önce Task.isCancelled değerini manuel olarak kontrol etmektir. URLSession için, görev iptal edildiğinde ağ istekleri otomatik olarak iptal edilir, çünkü URLSession Structured Concurrency'yi yerel olarak destekler.
.task birçok senaryo için uygundur: basit JSON yüklemeden TaskGroup ile karmaşık paralel işlemlere kadar. Üç tipik kullanım durumunu inceleyelim.
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("Yükleme başarısız")
}
}
.task {
defer { isLoading = false }
do {
profile = await APIClient().fetchProfile()
} catch {
// profil nil kalır
}
}
}
}
struct DashboardView: View {
@State var stats: DashboardStats?
var body: some View {
Text("Kontrol Paneli")
.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
}
}
}
En yaygın hata, ana iş parçacığına geçmeden .task içinde UI özelliklerini değiştirmektir. SwiftUI async bağlamında @State değiştirirken otomatik olarak güncellemeleri ana iş parçacığına döndürse de, .task içinde UIKit öğelerinin doğrudan manipülasyonu çökmeye neden olabilir.
// ❌ Hata: hata yönetimi yok
.task {
let data = await fetchData() // hata oluştuğunda çöker!
items = data
}
// ✅ Doğru: do/catch
.task {
do {
items = await fetchData()
} catch {
errorMessage = error.localizedDescription
}
}
Sıkça Sorulan Sorular
.task, View kaybolduğunda görevi otomatik olarak iptal eder ve Structured Concurrency'yi destekler. .onAppear'daki Task { }, göreve manuel olarak referans tutmayı ve .onDisappear'da cancel() çağırmayı gerektirir. .task ayrıca okunması daha kolaydır — veri yüklemenin View yaşam döngüsünün bir parçası olduğunu açıkça belirtir.
Evet, bir AsyncSequence veya AsyncStream'e abone olmak için .task içinde for await value in publisher.values kullanın. Bu, Publisher.values uzantısı aracılığıyla hem async/await hem de Combine ile çalışır. View kaybolduğunda, yineleme otomatik olarak sona erer ve abonelik iptal edilir.
Evet, TabView içindeki View her değişimde yeniden oluşturuluyorsa. iOS 18'den itibaren TabView, Views'ı bellekte tutabilir — bu durumda .task yeniden başlamaz. Her sekme değişiminde verileri yeniden yüklemeniz gerekiyorsa, sekme tanımlayıcısıyla .task(id:) kullanın.
SwiftUI, View kaybolduğunda görevi iptal eder. URLSession isteği görevin içindeyse, o da iptal edilir. Görev işbirlikçi iptali desteklemiyorsa (örneğin, isCancelled'i kontrol etmiyorsa), çalışmaya devam eder, ancak sonucu duruma uygulanmaz çünkü View artık mevcut değildir.
Evet, tek bir View'de birden fazla .task değiştiricisi ekleyebilirsiniz. Her biri bağımsız bir görev oluşturur. Bu, farklı veri kaynaklarını ayırmak için kullanışlıdır: profil yüklemek için bir .task, WebSocket'e abone olmak için ikincisi, coğrafi konumu izlemek için üçüncüsü.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun