Ana Sayfa / Blog / Observation framework: @Published’i ne zaman bıraktım

Observation framework: @Published’i ne zaman bıraktım

iOS 17’de gelen Observation framework’ünü 2025’in başında bir projede baştan beri kullandım. ObservableObject ve @Published yerine @Observable tercih etmenin ne getirdiğini, neyi kaybettiğimi paylaşayım. Motivasyon: gereksiz re-render ObservableObject ile bir property değiştiğinde objectWillChange publisher tetikleniyor ve view’ın tamamı re-render oluyor. @Published 10 property’li bir ViewModel’da sadece tek property’yi kullanan view bile güncelleniyor: class OldVM: ObservableObject […]

iOS 17’de gelen Observation framework’ünü 2025’in başında bir projede baştan beri kullandım. ObservableObject ve @Published yerine @Observable tercih etmenin ne getirdiğini, neyi kaybettiğimi paylaşayım.

Motivasyon: gereksiz re-render

ObservableObject ile bir property değiştiğinde objectWillChange publisher tetikleniyor ve view’ın tamamı re-render oluyor. @Published 10 property’li bir ViewModel’da sadece tek property’yi kullanan view bile güncelleniyor:

class OldVM: ObservableObject {
    @Published var query = ""
    @Published var results: [Item] = []
    @Published var isLoading = false
    // ... 7 more
}

struct SearchBar: View {
    @ObservedObject var vm: OldVM
    var body: some View {
        TextField("search", text: $vm.query)
        // isLoading değişince de re-render
    }
}

Observation ile property bazlı tracking yapılıyor. SwiftUI hangi property okunuyorsa sadece ona abone oluyor:

@Observable class NewVM {
    var query = ""
    var results: [Item] = []
    var isLoading = false
}

struct SearchBar: View {
    let vm: NewVM   // @ObservedObject yok
    var body: some View {
        TextField("search", text: Bindable(vm).query)
    }
}

Ölçtüğüm fark

Kompleks bir tablolu ekranda 50 satır var, her satırın kendi view modeli yok; tek büyük VM listeyi yönetiyor. @Published ile sadece bir satırın checkbox’ını tıkladığında 50 satır da re-render oluyordu. @Observable ile sadece ilgili satır güncelleniyor. Instruments’ta frame drop %60 azaldı.

Bindable kullanımı

@Published’in $vm.property binding syntax’ı @Observable’da @Bindable property wrapper ile geliyor:

struct EditView: View {
    @Bindable var user: User
    var body: some View {
        TextField("name", text: $user.name)
    }
}

Parametre olarak alıyorsan function scope’unda ayrı Bindable yaratman lazım:

struct Cell: View {
    let user: User
    var body: some View {
        @Bindable var bindable = user
        TextField("name", text: $bindable.name)
    }
}

Environment ile geçiş

Eski kodda @EnvironmentObject kullanılıyordu. Observation’da daha temiz bir API var; @Environment üzerinden observable object geçiriyorsun:

@Observable class AppState { var user: User? }

@main struct MyApp: App {
    @State var appState = AppState()
    var body: some Scene {
        WindowGroup {
            ContentView().environment(appState)
        }
    }
}

struct ContentView: View {
    @Environment(AppState.self) var appState
    var body: some View { Text(appState.user?.name ?? "") }
}

Combine’la entegrasyon

@Published publisher üretiyordu; Combine pipeline kuruyordun. @Observable bunu vermiyor. Observable bir property’yi publisher’a çevirmek için manuel iş yapman lazım:

import Observation
import Combine
let subject = PassthroughSubject<String, Never>()
withObservationTracking {
    _ = vm.query
} onChange: {
    subject.send(vm.query)
}

Bu pattern rebind gerektiriyor (her change’den sonra yeniden withObservationTracking çağırmalısın). İşi biraz daha el yordamı.

Bıraktığım alışkanlık: @StateObject vs @State

ObservableObject için @StateObject gerekiyordu (view lifecycle boyunca yaşayacak). @Observable için artık @State yeterli:

struct Screen: View {
    @State private var vm = MyVM()
    // eskiden @StateObject private var vm = MyVM()
}

Minimum iOS 17 şartı

Observation framework iOS 17+. Daha eski destekliyorsan ObservableObject’te kalman gerekiyor veya conditional compilation ile iki kod yolu tutman lazım. Kendi tercihim: proje minimum deployment’ını iOS 17’ye çekebiliyorsam çekiyorum. iOS 16 kullanıcı oranı 2026 ortasından itibaren küçük.

Pitfall: computed property tracking’i

Observation computed property’yi tek başına track etmiyor. Computed içindeki stored property’leri takip ediyor:

@Observable class VM {
    var items: [Item] = []
    var activeCount: Int { items.filter { $0.isActive }.count }
}

View vm.activeCount okuduğunda Observation items‘a abone oluyor. Bu genelde doğru davranış ama tracking’i debug ederken karışıklık yaratabiliyor.

Son notlar

Yeni projede @Observable varsayılanım. Mevcut projede kademeli olarak taşıyorum; aynı ekranda ObservableObject ve @Observable yan yana yaşıyor. Geçiş sırasında bug’a denk gelmedim; API yüzeyi uyumlu. Kaybettiğim tek şey Combine publisher’ları; ona da ihtiyacım zaten düşüyordu çünkü async/await’e geçtim. Net kazanç.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç