SwiftUI’da state yönetimi yıllar içinde değişti. iOS 17’de @Observable geldi, iOS 14’te @StateObject, başında sadece @State vardı. Junior iOS developer’larla çalışırken en çok karışan konu bu: hangi property wrapper hangi duruma uygun?
Pratik bir karar ağacı vereyim, her yeni proje başlangıcında aynı soruları soruyorum.
Soru 1: Bu veri tek bir view’a mı ait?
İki view arasında paylaşılmıyor, kaydetmiyor, bir başka yerde güncellenmiyor, tek view’ın içinde kalıyor. Kullanıcı bir toggle’a basıyor, bir metin alanına yazıyor, bir tab seçiyor.
Cevap: @State
Bu basit, performanslı ve doğru. Karmaşık bir şey düşünmeyin.
Soru 2: Veri 2+ view arasında paylaşılıyor mu?
Parent → Child’a veri geçiriyorsun, Child değiştiriyor ve Parent bu değişikliği görsün istiyorsun. Veya kardeş view’lar ortak bir state’i paylaşıyor.
Cevap: Parent’ta @State, Child’a @Binding. $ prefix’i binding oluşturuyor, değer değil, değer’e pointer gibi düşün.
Soru 3: Veri bir business logic class’ında mı?
View’dan bağımsız bir ViewModel, bir data loader, bir authentication manager. Class olmak zorunda (struct olmaz) çünkü identity önemli.
iOS 17+ ise: @Observable + @State
iOS 16 ve altı: ObservableObject + @Published properties + @StateObject
@Observable makro’su tüm property’leri otomatik olarak observation sistemine ekliyor, @Published yazmak zorunda değilsiniz. Bu ölçülebilir ölçüde daha performanslı çünkü sadece gerçekten okunan property’ler view’ı invalidate ediyor.
iOS 17+ hedefliyorsanız, @StateObject ve @ObservedObject‘i unutun. @Observable + @State (ownership için) yeterli.
Soru 4: Bu aynı nesne app boyunca paylaşılıyor mu?
Kullanıcı session’ı, tema ayarı, auth manager, global settings, nerede olursan ol aynı instance’a erişmek istiyorsun.
Cevap: @Environment. Parent’ta .environment(session) modifier’ı ile inject edersin, herhangi bir nested child’da @Environment(AppSession.self) var session ile alırsın.
Environment prop drilling’i çözüyor. 5 seviye nested bir view’da bile session’a erişebilirsin.
Soru 5: Veri disk’te kalıcı mı olmalı?
Kullanıcı ayarları, son açılan tarih, abonelik durumu, uygulama kapansa bile bu bilgiyi tutmak istiyorsun.
Küçük şeyler için: @AppStorage (UserDefaults wrapper). String, Int, Bool, Date gibi basit tipler için tek satırda.
Daha büyük veri (listeler, kullanıcı veri modelleri) için: CoreData, SwiftData, veya disk’te JSON.
Karıştırılan şey: @State vs @Binding
Yeni başlayanlar en çok bu ikisini karıştırıyor.
@State: “Ben bu veriyi yaratıyorum ve sahipleniyorum”@Binding: “Ben bu veriyi kullanıyorum ve parent’a geri bildiriyorum”
Bir view sadece kendi yarattığı veri için @State kullanmalı. Başkasının verdiği veri üzerinde değişiklik yapmak için @Binding.
Antipattern: @ObservedObject ile new instance her render’da
iOS 16 ve öncesinde gördüğüm çok yaygın hata: view’ın body’sinde @ObservedObject var viewModel = UserViewModel() gibi yazmak. Bu her view yeniden render olduğunda yeni bir UserViewModel yaratır. Animation’lar bozulur, data kaybolur.
Doğrusu @StateObject ile view’ın kendi bir instance’ı sahiplenmesidir. iOS 17+ ile @Observable + @State kombosu bu sorunu temiz bir şekilde çözüyor.
Sonuç
Basit bir karar ağacı:
- View’a özel veri →
@State - Parent-Child paylaşım →
@Binding - Business logic class →
@Observable+@State(iOS 17+) - App-wide state →
@Environment - Persistent small data →
@AppStorage
Bu 5 pattern ile SwiftUI’nın %95’ini halledersiniz. Geri kalan %5 için de, ihtiyaç ortaya çıkınca öğrenirsiniz.