Section 6/131 menit
6. withObservationTracking — Observasi Manual
6. withObservationTracking — Observasi Manual
withObservationTracking berguna ketika kita butuh observasi di luar SwiftUI — misalnya di UIKit, di layer business logic, atau untuk testing.
swift
import Observation
@Observable
final class CounterModel {
var count: Int = 0
var label: String = "Start"
}
// Observasi manual — cocok untuk UIKit atau non-SwiftUI context
func setupManualObservation(model: CounterModel, label: UILabel) {
func observe() {
withObservationTracking {
// Blok ini dijalankan SEGERA dan mencatat dependency.
// Semua property yang diakses di sini akan di-track.
label.text = "\(model.count)" // → track: count
} onChange: {
// Blok ini dipanggil SATU KALI ketika dependency berubah.
// Setelah dipanggil, tracking berhenti — harus daftar ulang.
// Ini adalah "fire-once" observer — berbeda dengan Combine publisher.
DispatchQueue.main.async {
observe() // Daftar ulang untuk melanjutkan tracking
}
}
}
observe()
}
Pola rekursif di atas adalah idiom standar untuk continuous observation dengan withObservationTracking. Setiap kali dependency berubah, kita daftar ulang observasi — sehingga tracking terus berjalan.
Integrasi UIKit dengan withObservationTracking
swift
// UIKit ViewController yang mengobservasi @Observable model
final class CounterViewController: UIViewController {
private let model = CounterModel()
private var observationTask: Task<Void, Never>?
private let countLabel = UILabel()
private let incrementButton = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
startObserving()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
observationTask?.cancel()
}
private func startObserving() {
// Bungkus dalam Task agar bisa di-cancel saat view hilang.
// Gunakan AsyncStream untuk mengubah "fire-once" withObservationTracking
// menjadi continuous async sequence.
observationTask = Task { @MainActor in
let stream = AsyncStream<Void> { continuation in
func scheduleObservation() {
withObservationTracking {
// Akses property yang ingin di-track
_ = self.model.count
_ = self.model.label
} onChange: {
continuation.yield(())
// Jadwal observasi ulang setelah yield
DispatchQueue.main.async { scheduleObservation() }
}
}
scheduleObservation()
continuation.onTermination = { _ in }
}
for await _ in stream {
guard !Task.isCancelled else { break }
// Update UI saat ada perubahan
countLabel.text = "\(model.count)"
title = model.label
}
}
}
@objc private func handleIncrement() {
model.count += 1
}
private func setupUI() {
// ... setup constraints
}
}