Observer — behaviorální vzor, ve kterém jeden objekt (vydavatel) informuje mnoho předplatitelů o změnách svého stavu. V mobilním vývoji je Observer základem reaktivních mechanismů: UI se přihlásí k odběru změn dat a automaticky se aktualizuje. Vzor je implementován v NotificationCenter na iOS a LiveData/Flow na Android. Více — na Refactoring Guru: Observer.
Hlavní body
Observer (pozorovatel) — behaviorální vzor GoF definující závislost «jeden k mnoha» mezi objekty. Když jeden objekt (Subject nebo Observable) změní stav, všechny závislé objekty (Observers) jsou automaticky informovány a aktualizovány. Vzor implementuje volnou vazbu: vydavatel nezná konkrétní třídy předplatitelů — pouze to, že implementují rozhraní Observer.
Struktura Observer zahrnuje rozhraní Subject s metodami attach(), detach(), notify() a rozhraní Observer s metodou update(). ConcreteSubject ukládá stav a seznam předplatitelů. ConcreteObserver implementuje update() a reaguje na změny. V mobilním vývoji se klasická implementace GoF vyskytuje zřídka — nahrazují ji vestavěné mechanismy: NotificationCenter, Combine, Flow, LiveData, které implementují stejnou myšlenku s moderním API.
Model Push vs Pull — v modelu Push posílá Subject data všem předplatitelům (NotificationCenter.post). V modelu Pull Subject pouze informuje a předplatitel si data sám vyžádá. Android LiveData používá Push (data jsou předávána v observe()), RxJava/Flow podporují oba modely. Volba závisí na úkolu: Push je jednodušší pro UI aktualizace, Pull je efektivnější pro velké objemy dat, které předplatitel možná nechce přijímat.
NotificationCenter — vestavěný mechanismus iOS/macOS pro implementaci Observer. Vydavatel odesílá Notification prostřednictvím NotificationCenter.default.post(name:, object:, userInfo:). Předplatitel se registruje pomocí addObserver(forName:, queue:, using:). NotificationCenter podporuje pojmenovaná oznámení (Notification.Name) a může předávat libovolná data v userInfo. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — systémové příklady.
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
}
// Vydavatel
NotificationCenter.default.post(
name: .userDidLogin,
object: nil,
userInfo: ["userId": "123"]
)
// Předplatitel
class ProfileViewModel {
private var observers: [NSObjectProtocol] = []
func startObserving() {
let observer = NotificationCenter.default.addObserver(
forName: .userDidLogin,
object: nil,
queue: .main
) { [weak self] notification in
guard let userId = notification.userInfo?["userId"] as? String else { return }
// Předplatitel reaguje na událost
self?.loadProfile(userId: userId)
}
observers.append(observer)
}
func stopObserving() {
observers.forEach { NotificationCenter.default.removeObserver($0) }
observers.removeAll()
}
}
Combine framework — moderní reaktivní alternativa k NotificationCenter, dostupná od iOS 13. Publisher (NotificationCenter, URLSession.Timer) — vydavatel, Subscriber (sink, assign) — předplatitel. Combine přidává operátory (map, filter, combineLatest) pro transformaci datového toku. @Published — property wrapper, který automaticky informuje předplatitele o změnách. V MVVM se SwiftUI nahrazuje Combine NotificationCenter pro propojení ViewModel a View.
KVO (Key-Value Observing) — starý mechanismus ObjC/Swift pro pozorování jednotlivých vlastností objektů. @objc dynamic var name: String — pozorovaná vlastnost. observe(.name) — předplatné. KVO funguje pouze s @objc-kompatibilními třídami a dědictvím ObjC. Apple doporučuje Combine a @Published místo KVO v nových projektech. KVO zůstává relevantní pro kompatibilitu UIKit v hybridních projektech.
LiveData — komponenta Android Architecture Components pro implementaci Observer. Třída Observable, která informuje předplatitele o změnách dat. LiveData zohledňuje životní cyklus: předplatitelé (LifecycleOwner) se automaticky odhlašují při zničení. LiveData je model Push: data jsou předávána v observe(). LiveData je základní stavební blok MVVM v Android před zavedením Jetpack Compose.
// ViewModel — vydavatel
class UserViewModel : ViewModel() {
private val _user = MutableLiveData<User?>(null)
val user: LiveData<User?> = _user
fun loadUser(id: String) {
viewModelScope.launch {
val result = userRepository.getUser(id)
_user.value = result
}
}
}
// Fragment — předplatitel
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.user.observe(viewLifecycleOwner) { user ->
// Předplatitel reaguje na změny
userName.text = user?.name
userEmail.text = user?.email
}
}
}
StateFlow a SharedFlow — reaktivní typy z Kotlin Coroutines, které nahradily LiveData v Jetpack Compose. StateFlow — observable state holder s pevnou aktuální hodnotou. SharedFlow — konfigurovatelný hot flow bez stavu, vhodný pro jednorázové události (navigace, toasty). Oba typy jsou úzce integrovány s Compose: collectAsState(), collectAsEffect(). StateFlow je povinný v moderních Android projektech s Compose.
LiveData vs StateFlow — LiveData je vázán na Android Lifecycle, StateFlow je platformově nezávislý. StateFlow podporuje coroutines, operátory (map, filter) a testuje se bez Android závislostí. LiveData je jednodušší pro kompatibilitu s Java. Google doporučuje StateFlow pro nové projekty s Kotlin + Compose, LiveData — pro podporu starých projektů nebo Java kódu.
Úniky paměti (memory leaks) — hlavní problém Observer bez správné správy předplatných. Pokud je předplatitel (Activity, Fragment, UIViewController) zničen, ale není odhlášen, vydavatel nadále drží referenci na něj a garbage collector nemůže uvolnit paměť. V Android musí LifecycleOwner (Activity/Fragment) zavolat removeObserver() nebo použít observe(viewLifecycleOwner). V iOS — removeObserver v deinit nebo disposeBag v Combine.
| Platforma | Mechanismus Observer | Automatické odhlášení | Ruční odhlášení |
|---|---|---|---|
| iOS | NotificationCenter | Ne | removeObserver() v deinit |
| iOS | Combine (sink) | Ne | store(in: &bag) — DisposeBag |
| iOS | KVO | Ne | removeObserver() v deinit |
| Android | LiveData | Ano (LifecycleOwner) | removeObserver() volitelně |
| Android | StateFlow | Přes viewModelScope | cancel() Job při odhlášení |
| Android | RxJava | Ne | dispose() v CompositeDisposable |
Slabá reference (weak reference) u předplatitelů — v bloku předplatného použijte [weak self] v Swift a odkazujte se na lifecycle scope v Kotlin. LiveData automaticky spravuje předplatné prostřednictvím LifecycleOwner — předplatné je aktivní pouze když je Lifecycle ve stavu STARTED nebo RESUMED. StateFlow v Compose používá collectAsState() s ohledem na lifecycle. NotificationCenter v iOS vyžaduje explicitní [weak self], protože closure silně referencuje self.
Observer (GoF) a Publisher-Subscriber (PubSub) — podobné, ale odlišné vzory. V Observer vydavatel přímo informuje předplatitele voláním jejich metod. Vydavatel ví o předplatitelích (ukládá seznam). V PubSub vydavatel a předplatitel nevědí jeden o druhém — mezi nimi je zprostředkovatel (Event Bus, Message Queue, NotificationCenter). Vydavatel posílá zprávu na kanál, předplatitel poslouchá kanál. PubSub poskytuje volnější vazbu.
Příklady PubSub v mobilním vývoji — NotificationCenter v iOS lze považovat za PubSub: vydavatel nezná předplatitele — pouze zveřejňuje oznámení. EventBus nebo Otto v Android (zastaralé). SharedFlow s BroadcastChannel — PubSub ve světě Kotlin. V distribuovaných systémech je PubSub implementován pomocí RabbitMQ, Kafka, Google PubSub. Pro mobilní vývoj je PubSub užitečný v modulární architektuře, kde moduly na sobě nemají záviset.
Co vybrat — pro UI aktualizace (ViewModel → View) použijte Observer (LiveData, StateFlow, @Published). Pro události mezi moduly (autorizace, odhlášení, změna motivu) — PubSub (SharedFlow, NotificationCenter, EventBus). Observer je jednodušší a efektivnější v rámci jedné obrazovky, PubSub je flexibilnější pro globální události, ale obtížnější na ladění kvůli implicitním závislostem.
Často kladené otázky
StateFlow je platformově nezávislý typ z Kotlin Coroutines, LiveData je vázán na Android Lifecycle. StateFlow podporuje coroutines a operátory, testuje se bez Android. LiveData automaticky spravuje předplatné prostřednictvím LifecycleOwner. Google doporučuje StateFlow pro nové projekty s Kotlin + Compose, LiveData — pro kompatibilitu s Java.
Použijte [weak self] v closure handleru a zavolejte removeObserver() v deinit. Uchovávejte referenci na observer (NSObjectProtocol) a odstraňte ji při zničení objektu. V Combine použijte AnyCancellable a store(in:) pro automatické odhlášení při uvolnění DisposeBag.
Ano, SwiftUI podporuje ObservableObject s @Published a @StateObject/@ObservedObject — to je vestavěná implementace Observer. @Published automaticky informuje View o změnách. Combine není povinný: ObservableObject používá Publisher objectWillChange zabudovaný do SwiftUI. Combine přidává operátory pro transformaci toků.
SharedFlow — pro jednorázové události (navigace, toasty, Snackbar), kde není potřeba aktuální hodnota. StateFlow — pro stav UI (seznam dat, průběh načítání), kde je potřeba aktuální snímek. SharedFlow nemá vlastnost value a nevrací poslední hodnotu novým předplatitelům.
KVO je zastaralý mechanismus ObjC, vyžaduje @objc dynamic a funguje pouze s třídami dědícími z NSObject. Combine je moderní Swift framework, typově bezpečný, s operátory a integrací se SwiftUI. Combine nahrazuje KVO a NotificationCenter. Apple doporučuje Combine pro nové projekty, KVO — pouze pro podporu starého kódu.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také