Observer — Key Concepts of the Subscription Pattern for Changes

Author: IT Sectr Published: 2026-02-17 Reading time: 7 min

Observer — a behavioral pattern in which one object (the publisher) notifies multiple subscribers about changes in its state. In mobile development, Observer is the foundation of reactive mechanics: the UI subscribes to data changes and automatically updates. The pattern is implemented in NotificationCenter on iOS and LiveData/Flow on Android. More details at Refactoring Guru: Observer.

Key Takeaways

  • Observer — a subscription pattern: one publisher, many subscribers
  • NotificationCenter — built-in Observer implementation in iOS/macOS
  • Flow and LiveData — reactive Observer implementations in Android
  • Push vs Pull — the publisher can send data or notify about events
  • Memory leaks — subscribers must unsubscribe to prevent leaks

What is Observer: the essence of the observer pattern?

Observer — a behavioral GoF pattern that defines a one-to-many dependency between objects. When one object (Subject or Observable) changes its state, all dependent objects (Observers) are automatically notified and updated. The pattern implements loose coupling: the publisher does not know the specific classes of subscribers — only that they implement the Observer interface.

Observer structure includes the Subject interface with attach(), detach(), notify() methods and the Observer interface with an update() method. ConcreteSubject stores the state and the subscriber list. ConcreteObserver implements update() and reacts to changes. In mobile development, the classic GoF implementation is rare — it is replaced by built-in mechanisms: NotificationCenter, Combine, Flow, LiveData, which implement the same idea with modern APIs.

Push vs Pull model — in the Push model, the Subject sends data to all subscribers (NotificationCenter.post). In the Pull model, the Subject only notifies, and the subscriber requests the data itself. Android LiveData uses Push (data is passed in observe()), RxJava/Flow support both models. The choice depends on the task: Push is simpler for UI updates, Pull is more efficient for large volumes of data that the subscriber may not want to receive.

Observer in iOS: NotificationCenter, Combine and KVO

NotificationCenter — a built-in iOS/macOS mechanism for implementing Observer. The publisher sends a Notification via NotificationCenter.default.post(name:, object:, userInfo:). The subscriber registers via addObserver(forName:, queue:, using:). NotificationCenter supports named notifications (Notification.Name) and can pass any data in userInfo. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — system examples.

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

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

// Subscriber
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 }
            // Subscriber reacts to the event
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

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

Combine framework — a modern reactive alternative to NotificationCenter, introduced in iOS 13. Publisher (NotificationCenter, URLSession, Timer) — the publisher, Subscriber (sink, assign) — the subscriber. Combine adds operators (map, filter, combineLatest) for data stream transformation. @Published — a property wrapper that automatically notifies subscribers of changes. In MVVM with SwiftUI, Combine replaces NotificationCenter for binding ViewModel and View.

KVO (Key-Value Observing) — an older ObjC/Swift mechanism for observing individual object properties. @objc dynamic var name: String — an observable property. observe(.name) — subscription. KVO only works with @objc-compatible classes and ObjC inheritance. Apple recommends Combine and @Published instead of KVO in new projects. KVO remains relevant for UIKit compatibility in hybrid projects.

Observer in Android: LiveData, StateFlow and SharedFlow

LiveData — a component of Android Architecture Components for implementing Observer. An observable class that notifies subscribers of data changes. LiveData is lifecycle-aware: subscribers (LifecycleOwner) automatically unsubscribe when destroyed. LiveData uses the Push model: data is passed in observe(). LiveData is the basic building block of MVVM in Android before Jetpack Compose.

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // Subscriber reacts to changes
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlow and SharedFlow — reactive types from Kotlin Coroutines that replaced LiveData in Jetpack Compose. StateFlow — an observable state holder with a fixed current value. SharedFlow — a configurable hot flow without state, suitable for one-shot events (navigation, toasts). Both types are tightly integrated with Compose: collectAsState(), collectAsEffect(). StateFlow is mandatory in modern Android projects with Compose.

LiveData vs StateFlow — LiveData is tied to Android Lifecycle, StateFlow is platform-independent. StateFlow supports coroutines, operators (map, filter) and can be tested without Android dependencies. LiveData is simpler for Java compatibility. Google recommends StateFlow for new projects on Kotlin + Compose, LiveData for maintaining legacy projects or Java code.

Subscription management and memory leaks

Memory leaks — the main problem with Observer without proper subscription management. If a subscriber (Activity, Fragment, UIViewController) is destroyed but not unsubscribed, the publisher continues to hold a reference to it, and the garbage collector cannot free the memory. In Android, LifecycleOwner (Activity/Fragment) should call removeObserver() or use observe(viewLifecycleOwner). In iOS — removeObserver in deinit or disposeBag in Combine.

PlatformObserver MechanismAutomatic UnsubscriptionManual Unsubscription
iOSNotificationCenterNoremoveObserver() in deinit
iOSCombine (sink)Nostore(in: &bag) — DisposeBag
iOSKVONoremoveObserver() in deinit
AndroidLiveDataYes (LifecycleOwner)removeObserver() optional
AndroidStateFlowVia viewModelScopecancel() Job on unsubscribe
AndroidRxJavaNodispose() in CompositeDisposable

Weak reference in subscribers — when using closures in subscription blocks, use [weak self] in Swift and reference lifecycle scopes in Kotlin. LiveData automatically manages subscriptions through LifecycleOwner — the subscription is active only when the Lifecycle is in the STARTED or RESUMED state. StateFlow in Compose uses collectAsState() with lifecycle awareness. NotificationCenter in iOS requires explicit [weak self] because the closure strongly references self.

Observer vs Publisher-Subscriber: what's the difference

Observer (GoF) and Publisher-Subscriber (PubSub) — similar but different patterns. In Observer, the publisher directly notifies subscribers by calling their methods. The publisher knows about subscribers (stores a list). In PubSub, the publisher and subscriber do not know each other — a mediator (Event Bus, Message Queue, NotificationCenter) sits between them. The publisher sends a message to a channel, the subscriber listens to the channel. PubSub has looser coupling.

Examples of PubSub in mobile development — NotificationCenter in iOS can be considered PubSub: the publisher does not know the subscribers — it simply posts a notification. EventBus or Otto in Android (deprecated). SharedFlow with BroadcastChannel — PubSub in the Kotlin world. In distributed systems, PubSub is implemented via RabbitMQ, Kafka, Google PubSub. For mobile development, PubSub is useful in modular architecture where modules should not depend on each other.

Which to choose — for UI updates (ViewModel → View), use Observer (LiveData, StateFlow, @Published). For cross-module events (authentication, logout, theme change) — PubSub (SharedFlow, NotificationCenter, EventBus). Observer is simpler and more efficient within a single screen, PubSub is more flexible for global events but harder to debug due to implicit dependencies.

Frequently Asked Questions

How is StateFlow different from LiveData?

StateFlow is a platform-independent type from Kotlin Coroutines, while LiveData is tied to the Android Lifecycle. StateFlow supports coroutines and operators, and can be tested without Android. LiveData automatically manages subscriptions through LifecycleOwner. Google recommends StateFlow for new projects on Kotlin + Compose, and LiveData for Java compatibility.

How to avoid memory leaks with NotificationCenter?

Use [weak self] in the handler closure and call removeObserver() in deinit. Store a reference to the observer (NSObjectProtocol) and remove it when the object is destroyed. In Combine, use AnyCancellable and store(in:) for automatic unsubscription when the DisposeBag is released.

Can Observer be used in SwiftUI without Combine?

Yes, SwiftUI supports ObservableObject with @Published and @StateObject/@ObservedObject — this is a built-in Observer implementation. @Published automatically notifies the View of changes. Combine is not required: ObservableObject uses the objectWillChange Publisher built into SwiftUI. Combine adds operators for stream transformation.

When to use SharedFlow instead of StateFlow?

SharedFlow — for one-shot events (navigation, toasts, Snackbar) where no current value is needed. StateFlow — for UI state (data list, loading progress) where a current snapshot is needed. SharedFlow has no value property and does not return the last value to new subscribers.

What's the difference between KVO and Combine in iOS?

KVO is a legacy ObjC mechanism, requires @objc dynamic and only works with classes inherited from NSObject. Combine is a modern Swift framework, type-safe, with operators and SwiftUI integration. Combine replaces KVO and NotificationCenter. Apple recommends Combine for new projects, KVO only for legacy support.

Summary

  • Observer — a behavioral pattern for notifying subscribers about changes
  • iOS NotificationCenter — PubSub implementation with named notifications
  • iOS Combine — a reactive framework with Publisher and Subscriber
  • Android LiveData — lifecycle-aware Observer from Android Architecture Components
  • Android StateFlow — a reactive State holder for Compose and coroutines
  • Memory management — mandatory unsubscription to prevent leaks
  • Observer vs PubSub — direct subscription vs mediator for loose coupling

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also