Tüm Yazılar
KategoriSwift
Okuma Süresi
21 dk okuma
Yayın Tarihi
...
Kelime Sayısı
1.574kelime

Kahveni hazırla - bu içerikli bir makale!

@Observable macro, ObservableObject'den gecis, performans avantajlari, Combine karsilastirmasi ve best practices.

Swift Observation Framework: @Observable ile Modern State Management

iOS 17 ile gelen Observation framework, SwiftUI'da state management'i koklunden degistirdi. @Observable macro sayesinde artik @Published, @StateObject ve @ObservedObject gibi wrapper'lara ihtiyac kalmadi. Bu rehberde Observation framework'un calisma mantigi, migration stratejileri ve performans avantajlarini kesfedeceksiniz.


Icindekiler


1. Neden Observation Framework?

Eski ObservableObject yaklasiminda ciddi performans sorunlari vardi:

Problem: Gereksiz View Guncellemesi

swift
1// ESKI YAKLASIM — ObservableObject
2class UserViewModel: ObservableObject {
3 @Published var name: String = ""
4 @Published var email: String = ""
5 @Published var avatar: UIImage?
6 @Published var settings: UserSettings = .default
7 @Published var notifications: [Notification] = []
8}
9 
10// Bu view SADECE name kullaniyor ama
11// email, avatar, settings, notifications degistiginde de
12// YENIDEN CIZILIYOR!
13struct UserNameView: View {
14 @ObservedObject var viewModel: UserViewModel
15 
16 var body: some View {
17 Text(viewModel.name) // Sadece name kullaniliyor
18 }
19}

2. Temel Kullanim

swift
1import Observation
2 
3// YENI YAKLASIM — @Observable
4@Observable
5class UserViewModel {
6 var name: String = ""
7 var email: String = ""
8 var avatar: UIImage?
9 var settings: UserSettings = .default
10 var notifications: [Notification] = []
11 
12 // Computed property'ler de otomatik izlenir
13 var displayName: String {
14 name.isEmpty ? "Misafir" : name
15 }
16}
17 
18// Bu view SADECE name degistiginde yeniden cizilir!
19struct UserNameView: View {
20 var viewModel: UserViewModel // @ObservedObject gerekmez!
21 
22 var body: some View {
23 Text(viewModel.displayName)
24 }
25}

@State ile Kullanim

swift
1struct ContentView: View {
2 @State private var viewModel = UserViewModel()
3 
4 var body: some View {
5 VStack {
6 TextField("Isim", text: $viewModel.name)
7 TextField("Email", text: $viewModel.email)
8 
9 // Alt view'e sadece referans gecir
10 UserNameView(viewModel: viewModel)
11 UserEmailView(viewModel: viewModel)
12 }
13 }
14}
15 
16// Sadece email degistiginde guncellenir
17struct UserEmailView: View {
18 var viewModel: UserViewModel
19 
20 var body: some View {
21 Text(viewModel.email)
22 }
23}

3. ObservableObject vs @Observable Karsilastirma

Ozellik
ObservableObject
@Observable
**Import**
Combine
Observation
**Property Wrapper**
@Published gerekli
Otomatik
**View Guncelleme**
Tum property'ler
Sadece kullanilan
**Binding**
$viewModel.prop
$viewModel.prop
**Nesting**
Sorunlu
Sorunsuz
**Performans**
O(n) guncelleme
O(1) guncelleme
**Min. iOS**
13+
17+
**View Kullanim**
@StateObject/@ObservedObject
@State veya plain

Performans Farki Ornegi

swift
1// Senaryo: 100 property'li bir model
2// ObservableObject: Herhangi bir property degisiminde TUM view'ler guncellenir
3// @Observable: Sadece ilgili property'yi kullanan view'ler guncellenir
4 
5// ObservableObject — 1 degisiklik = 100 view guncelleme (worst case)
6// @Observable — 1 degisiklik = 1 view guncelleme (best case)

4. Migration Stratejisi

Adim 1: Import Degistir

swift
1// Oncesi
2import Combine
3 
4class SettingsStore: ObservableObject {
5 @Published var theme: Theme = .light
6 @Published var fontSize: CGFloat = 16
7 @Published var language: String = "tr"
8}
9 
10// Sonrasi
11import Observation
12 
13@Observable
14class SettingsStore {
15 var theme: Theme = .light
16 var fontSize: CGFloat = 16
17 var language: String = "tr"
18}

Adim 2: View Wrapper'lari Temizle

swift
1// Oncesi
2struct SettingsView: View {
3 @StateObject private var store = SettingsStore()
4 
5 var body: some View {
6 ChildView(store: store)
7 }
8}
9 
10struct ChildView: View {
11 @ObservedObject var store: SettingsStore
12 // ...
13}
14 
15// Sonrasi
16struct SettingsView: View {
17 @State private var store = SettingsStore()
18 
19 var body: some View {
20 ChildView(store: store)
21 }
22}
23 
24struct ChildView: View {
25 var store: SettingsStore // Plain property!
26 // ...
27}

Adim 3: Environment Gecisi

swift
1// Oncesi
2struct ParentView: View {
3 @StateObject private var store = SettingsStore()
4 var body: some View {
5 ChildView()
6 .environmentObject(store)
7 }
8}
9 
10struct ChildView: View {
11 @EnvironmentObject var store: SettingsStore
12 // ...
13}
14 
15// Sonrasi
16struct ParentView: View {
17 @State private var store = SettingsStore()
18 var body: some View {
19 ChildView()
20 .environment(store)
21 }
22}
23 
24struct ChildView: View {
25 @Environment(SettingsStore.self) private var store
26 // ...
27}

5. Performans Avantajlari

Observation framework property-level tracking yapar. SwiftUI, view'in body'sinde hangi property'lere erisildigini izler ve sadece o property'ler degistiginde view'i yeniden cizer.

swift
1@Observable
2class DashboardData {
3 var temperature: Double = 0 // Sensor A
4 var humidity: Double = 0 // Sensor B
5 var pressure: Double = 0 // Sensor C
6 var windSpeed: Double = 0 // Sensor D
7 var lastUpdate: Date = .now // Her saniye guncellenir
8}
9 
10// Sadece temperature degistiginde guncellenir
11struct TemperatureWidget: View {
12 var data: DashboardData
13 var body: some View {
14 Text("\(data.temperature, specifier: "%.1f")°C")
15 }
16}
17 
18// Sadece humidity degistiginde guncellenir
19struct HumidityWidget: View {
20 var data: DashboardData
21 var body: some View {
22 Text("\(data.humidity, specifier: "%.0f")%")
23 }
24}
25// lastUpdate her saniye degisse bile bu widget'lar etkilenmez!

6. Gelismis Kaliplar

withObservationTracking

swift
1import Observation
2 
3@Observable
4class Counter {
5 var count = 0
6}
7 
8let counter = Counter()
9 
10// Manuel observation tracking
11withObservationTracking {
12 // Bu blokta erisilen property'ler izlenir
13 print("Count: \(counter.count)")
14} onChange: {
15 // counter.count degistiginde cagirilir (bir kez!)
16 print("Count degisti!")
17}
18 
19counter.count = 42 // "Count degisti!" yazdirir
20counter.count = 43 // Artik izlenmiyor (tek seferlik)

@ObservationIgnored

swift
1@Observable
2class CacheManager {
3 var items: [String: Any] = [:]
4 
5 @ObservationIgnored
6 var internalCache: NSCache<NSString, AnyObject> = .init()
7 // internalCache degisiklikleri view guncellemesine neden olmaz
8 
9 @ObservationIgnored
10 private var cancellables = Set<AnyCancellable>()
11}

7. Combine ile Birlikte Kullanim

swift
1import Observation
2import Combine
3 
4@Observable
5class SearchViewModel {
6 var query: String = ""
7 var results: [SearchResult] = []
8 var isLoading = false
9 
10 @ObservationIgnored
11 private var cancellables = Set<AnyCancellable>()
12 
13 init() {
14 // Combine pipeline hala kullanilabilir
15 setupSearchDebounce()
16 }
17 
18 private func setupSearchDebounce() {
19 // Timer-based debounce icin Combine hala faydali
20 Timer.publish(every: 0.3, on: .main, in: .common)
21 .autoconnect()
22 .sink { [weak self] _ in
23 guard let self = self else { return }
24 Task {
25 await self.performSearch()
26 }
27 }
28 .store(in: &cancellables)
29 }
30 
31 private func performSearch() async {
32 guard !query.isEmpty else {
33 results = []
34 return
35 }
36 isLoading = true
37 defer { isLoading = false }
38 // API cagrisi...
39 }
40}

8. Test Stratejileri

swift
1import Testing
2import Observation
3 
4@Suite("UserViewModel Tests")
5struct UserViewModelTests {
6 
7 @Test("Name degisikligi displayName'i gunceller")
8 func nameUpdatesDisplayName() {
9 let vm = UserViewModel()
10 vm.name = "Ahmet"
11 #expect(vm.displayName == "Ahmet")
12 }
13 
14 @Test("Bos isim 'Misafir' dondurur")
15 func emptyNameReturnsMisafir() {
16 let vm = UserViewModel()
17 vm.name = ""
18 #expect(vm.displayName == "Misafir")
19 }
20 
21 @Test("Observation tracking calisiyor")
22 func observationTracking() async {
23 let vm = UserViewModel()
24 var changed = false
25 
26 withObservationTracking {
27 _ = vm.name
28 } onChange: {
29 changed = true
30 }
31 
32 vm.name = "Test"
33 #expect(changed == true)
34 }
35}

9. Best Practices

  1. Yeni projeler icin @Observable kullanin — iOS 17+ hedefliyorsaniz
  2. @ObservationIgnored ile gereksiz tracking'i onleyin — cache, cancellables
  3. Computed property'leri kullanin — otomatik izlenir
  4. Nesting'i korkmadan kullanin — ic ice @Observable nesneler sorunsuz
  5. Environment ile dagitin — .environment(store) kullanin
  6. Combine'i tamamen birakmayin — debounce, merge gibi reactive islemler icin hala faydali

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Tebrikler! Bu yaziyi sonuna kadar okudugun icin sana ozel bir hediyem var:

10. Sonuc

Observation framework, SwiftUI state management icin buyuk bir adim. Property-level tracking ile performansi artirirken, basitlestirrilmis API ile gelistirici deneyimini de iyilestiriyor. iOS 17+ hedefleyen tum projelerde @Observable'a gecis yapmanizi siddetle oneriyoruz.


Sonuc ve Oneriler

Observation framework, SwiftUI'nin state management yaklasiminda bir paradigma degisikligini temsil eder. ObservableObject ve @Published ile calisan mevcut yapilarda her property degisikligi tum subscriber'lari tetiklerken, @Observable macro'su property-level granularity ile yalnizca gercekten degisen degerri kullanan view'lari gunceller. Bu fark, ozellikle karmasik veri modelleri olan uygulamalarda dramatik performans iyilesmesi saglar.

Migration sureci planlarken once en cok view guncelleme tetikleyen model siniflarinizi belirleyin ve bunlari @Observable'a donusturun. @ObservationIgnored ile izlenmemesi gereken property'leri isaretlemeyi unutmayin. withObservationTracking fonksiyonu ile UIKit entegrasyonu yaparak hibrit projelerde de Observation'dan faydalanabilirsiniz.

Test yazarken @Observable siniflarin davranisini dogrulamak icin XCTest ile birlikte property degisikliklerini takip eden yardimci fonksiyonlar olusturun. Bu yaklasim, hem state yonetiminin dogrulugunu garanti eder hem de gelecekteki refactoring'lerde guvenlik agi saglar.

Etiketler

#Swift#Observation#Observable#State Management#SwiftUI#Macro
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz