Observer — kluczowe pojęcia wzorca subskrypcji zmian

Autor: IT Sectr Opublikowano: 2026-02-17 Czas czytania: 7 min

Observer — wzorzec behawioralny, w którym jeden obiekt (wydawca) powiadamia wielu subskrybentów o zmianach swojego stanu. W programowaniu mobilnym Observer leży u podstaw mechanik reaktywnych: UI subskrybuje zmiany danych i automatycznie się aktualizuje. Wzorzec jest zaimplementowany w NotificationCenter na iOS i LiveData/Flow na Android. Więcej — na Refactoring Guru: Observer.

Najważniejsze

  • Observer — wzorzec subskrypcji: jeden wydawca, wielu subskrybentów
  • NotificationCenter — wbudowana implementacja Observer w iOS/macOS
  • Flow i LiveData — reaktywne implementacje Observer w Android
  • Push vs Pull — wydawca może wysyłać dane lub powiadamiać o zdarzeniach
  • Wycieki pamięci — subskrybenci muszą się wypisywać, aby zapobiec wyciekom

Czym jest Observer: istota wzorca obserwator?

Observer (obserwator) — wzorzec behawioralny GoF definiujący zależność „jeden do wielu" między obiektami. Gdy jeden obiekt (Subject lub Observable) zmienia stan, wszystkie zależne obiekty (Observers) są automatycznie powiadamiane i aktualizowane. Wzorzec realizuje słabe powiązanie: wydawca nie zna konkretnych klas subskrybentów — tylko to, że implementują interfejs Observer.

Struktura Observer obejmuje interfejs Subject z metodami attach(), detach(), notify() oraz interfejs Observer z metodą update(). ConcreteSubject przechowuje stan i listę subskrybentów. ConcreteObserver implementuje update() i reaguje na zmiany. W programowaniu mobilnym klasyczna implementacja GoF występuje rzadko — zastępują ją wbudowane mechanizmy: NotificationCenter, Combine, Flow, LiveData, które realizują tę samą ideę z nowoczesnym API.

Model Push vs Pull — w modelu Push Subject wysyła dane do wszystkich subskrybentów (NotificationCenter.post). W modelu Pull Subject tylko powiadamia, a subskrybent sam pobiera dane. Android LiveData używa Push (dane są przekazywane w observe), RxJava/Flow obsługują oba modele. Wybór zależy od zadania: Push jest prostszy dla aktualizacji UI, Pull wydajniejszy dla dużych wolumenów danych, które subskrybent może nie chcieć otrzymywać.

Observer w iOS: NotificationCenter, Combine i KVO

NotificationCenter — wbudowany mechanizm iOS/macOS do implementacji Observer. Wydawca wysyła Notification przez NotificationCenter.default.post(name:, object:, userInfo:). Subskrybent rejestruje się przez addObserver(forName:, queue:, using:). NotificationCenter obsługuje nazwane powiadomienia (Notification.Name) i może przekazywać dowolne dane w userInfo. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — systemowe przykłady.

swift
extension Notification.Name {
    static let userDidLogin = Notification.Name("userDidLogin")
}

// Wydawca
NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "123"]
)

// Subskrybent
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 }
            // Subskrybent reaguje na zdarzenie
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

    func stopObserving() {
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
    }
}

Combine framework — nowoczesna reaktywna alternatywa dla NotificationCenter, dostępna od iOS 13. Publisher (NotificationCenter, URLSession.Timer) — wydawca, Subscriber (sink, assign) — subskrybent. Combine dodaje operatory (map, filter, combineLatest) do transformacji strumienia danych. @Published — property wrapper automatycznie powiadamiający subskrybentów o zmianach. W MVVM z SwiftUI Combine zastępuje NotificationCenter do łączenia ViewModel i View.

KVO (Key-Value Observing) — stary mechanizm ObjC/Swift do obserwacji pojedynczych właściwości obiektów. @objc dynamic var name: String — obserwowana właściwość. observe(.name) — subskrypcja. KVO działa tylko z klasami zgodnymi z @objc i dziedzictwem ObjC. Apple zaleca Combine i @Published zamiast KVO w nowych projektach. KVO pozostaje aktualny dla zgodności z UIKit w projektach hybrydowych.

Observer w Android: LiveData, StateFlow i SharedFlow

LiveData — komponent Android Architecture Components do implementacji Observer. Klasa Observable, która powiadamia subskrybentów o zmianach danych. LiveData uwzględnia cykl życia: subskrybenci (LifecycleOwner) automatycznie wypisują się przy zniszczeniu. LiveData to model Push: dane są przekazywane w observe(). LiveData to podstawowy blok budulcowy MVVM w Android przed wprowadzeniem Jetpack Compose.

kotlin
// ViewModel — wydawca
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 — subskrybent
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // Subskrybent reaguje na zmiany
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlow i SharedFlow — reaktywne typy z Kotlin Coroutines, które zastąpiły LiveData w Jetpack Compose. StateFlow — observable state holder z ustaloną bieżącą wartością. SharedFlow — konfigurowalny hot flow bez stanu, odpowiedni do jednorazowych zdarzeń (nawigacja, toasty). Oba typy są ściśle zintegrowane z Compose: collectAsState(), collectAsEffect(). StateFlow jest obowiązkowy w nowoczesnych projektach Android z Compose.

LiveData vs StateFlow — LiveData jest związany z Android Lifecycle, StateFlow jest platformoniezależny. StateFlow obsługuje coroutines, operatory (map, filter) i jest testowalny bez zależności Android. LiveData jest prostszy dla zgodności z Java. Google zaleca StateFlow dla nowych projektów na Kotlin + Compose, LiveData — dla wsparcia starych projektów lub kodu Java.

Zarządzanie subskrypcjami i wycieki pamięci

Wycieki pamięci (memory leaks) — główny problem Observer bez prawidłowego zarządzania subskrypcjami. Jeśli subskrybent (Activity, Fragment, UIViewController) został zniszczony, ale nie wypisał się, wydawca nadal trzyma do niego referencję, a garbage collector nie może zwolnić pamięci. W Android LifecycleOwner (Activity/Fragment) powinien wywołać removeObserver() lub użyć observe(viewLifecycleOwner). W iOS — removeObserver w deinit lub disposeBag w Combine.

PlatformaMechanizm ObserverAutomatyczne wypisywanieRęczne wypisywanie
iOSNotificationCenterNieremoveObserver() w deinit
iOSCombine (sink)Niestore(in: &bag) — DisposeBag
iOSKVONieremoveObserver() w deinit
AndroidLiveDataTak (LifecycleOwner)removeObserver() opcjonalnie
AndroidStateFlowPrzez viewModelScopecancel() Job przy wypisywaniu
AndroidRxJavaNiedispose() w CompositeDisposable

Słaba referencja (weak reference) w subskrybentach — w bloku subskrypcji używaj [weak self] w Swift i odwołuj się do lifecycle scope w Kotlin. LiveData automatycznie zarządza subskrypcją przez LifecycleOwner — subskrypcja jest aktywna tylko gdy Lifecycle jest w stanie STARTED lub RESUMED. StateFlow w Compose używa collectAsState() z uwzględnieniem lifecycle. NotificationCenter w iOS wymaga jawnego [weak self], ponieważ domknięcie silnie referencjonuje self.

Observer vs Publisher-Subscriber: jaka jest różnica

Observer (GoF) i Publisher-Subscriber (PubSub) — podobne, ale różne wzorce. W Observer wydawca bezpośrednio powiadamia subskrybentów przez wywołanie ich metod. Wydawca wie o subskrybentach (przechowuje listę). W PubSub wydawca i subskrybent nie wiedzą o sobie nawzajem — między nimi znajduje się pośrednik (Event Bus, Message Queue, NotificationCenter). Wydawca wysyła wiadomość na kanał, subskrybent słucha kanału. PubSub zapewnia słabsze powiązanie.

Przykłady PubSub w programowaniu mobilnym — NotificationCenter w iOS można uznać za PubSub: wydawca nie zna subskrybentów — po prostu publikuje powiadomienie. EventBus lub Otto w Android (przestarzałe). SharedFlow z BroadcastChannel — PubSub w świecie Kotlin. W systemach rozproszonych PubSub jest realizowany przez RabbitMQ, Kafka, Google PubSub. W programowaniu mobilnym PubSub jest przydatny w architekturze modułowej, gdzie moduły nie powinny zależeć od siebie.

Co wybrać — do aktualizacji UI (ViewModel → View) używaj Observer (LiveData, StateFlow, @Published). Do zdarzeń międzymodułowych (autoryzacja, wylogowanie, zmiana motywu) — PubSub (SharedFlow, NotificationCenter, EventBus). Observer jest prostszy i wydajniejszy w obrębie jednego ekranu, PubSub jest bardziej elastyczny dla zdarzeń globalnych, ale trudniejszy w debugowaniu z powodu niejawnych zależności.

Często zadawane pytania

Czym StateFlow różni się od LiveData?

StateFlow to platformoniezależny typ z Kotlin Coroutines, LiveData jest związany z Android Lifecycle. StateFlow obsługuje coroutines i operatory, testuje się bez Android. LiveData automatycznie zarządza subskrypcją przez LifecycleOwner. Google zaleca StateFlow dla nowych projektów na Kotlin + Compose, LiveData — dla zgodności z Java.

Jak uniknąć wycieków pamięci z NotificationCenter?

Używaj [weak self] w domknięciu handlera i wywołuj removeObserver() w deinit. Przechowuj referencję do observer (NSObjectProtocol) i usuwaj go przy zniszczeniu obiektu. W Combine używaj AnyCancellable i store(in:) do automatycznego wypisywania przy zwolnieniu DisposeBag.

Czy można używać Observer w SwiftUI bez Combine?

Tak, SwiftUI obsługuje ObservableObject z @Published i @StateObject/@ObservedObject — to wbudowana implementacja Observer. @Published automatycznie powiadamia View o zmianach. Combine nie jest wymagany: ObservableObject używa wbudowanego w SwiftUI wydawcy objectWillChange Publisher. Combine dodaje operatory do transformacji strumieni.

Kiedy używać SharedFlow zamiast StateFlow?

SharedFlow — do jednorazowych zdarzeń (nawigacja, toasty, Snackbar), gdzie nie jest potrzebna bieżąca wartość. StateFlow — do stanu UI (lista danych, postęp ładowania), gdzie potrzebny jest bieżący snapshot. SharedFlow nie ma właściwości value i nie zwraca ostatniej wartości nowym subskrybentom.

Jaka jest różnica między KVO a Combine w iOS?

KVO to przestarzały mechanizm ObjC, wymaga @objc dynamic i działa tylko z klasami dziedziczącymi po NSObject. Combine to nowoczesny framework Swift, typowo bezpieczny, z operatorami i integracją ze SwiftUI. Combine zastępuje KVO i NotificationCenter. Apple zaleca Combine dla nowych projektów, KVO — tylko do wsparcia starszego kodu.

Podsumowanie

  • Observer — wzorzec behawioralny do powiadamiania subskrybentów o zmianach
  • iOS NotificationCenter — implementacja PubSub z nazwanymi powiadomieniami
  • iOS Combine — reaktywny framework z Publisher i Subscriber
  • Android LiveData — lifecycle-aware Observer z Android Architecture Components
  • Android StateFlow — reaktywny state holder dla Compose i korutyn
  • Zarządzanie pamięcią — obowiązkowe wypisywanie w celu zapobiegania wyciekom
  • Observer vs PubSub — bezpośrednia subskrypcja vs pośrednik dla słabego powiązania

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również