โ† Back to Design Patterns
๐Ÿ‘๏ธ
Behavioral

Observer

Defines a one-to-many dependency so that when one object changes state, all its dependents are notified automatically.

The Problem

Multiple parts of an app need to react when some shared data changes, but you don't want the data source to be aware of or tightly coupled to its consumers.

How It Works

  • โ€บSubject (publisher) maintains a list of observers (subscribers)
  • โ€บLoose coupling: subject does not know concrete observer classes
  • โ€บPush model sends data with the notification; pull model lets observers fetch it
  • โ€บRisk of memory leaks if observers are not unsubscribed (use weak references)
  • โ€บAlso known as Publish/Subscribe or Event Listener pattern

When to Use

โœ“UI that must update when underlying data changes (data binding)
โœ“Event systems where multiple components react to the same event
โœ“Notification systems, analytics tracking, logging across multiple targets
โœ“Reactive programming foundations (Combine, RxSwift, LiveData)

Real-World Examples

๐ŸŽ
iOS

NotificationCenter, Combine Publisher/Subscriber, KVO, @Published + @ObservedObject

๐Ÿค–
Android

LiveData.observe(), StateFlow.collect(), RxJava Observable, EventBus

๐ŸŒ
Web

EventEmitter (Node.js), RxJS Observable, Vue reactivity, React useEffect

Code Examples

Swift
protocol StoreObserver: AnyObject {
    func storeDidUpdate<T>(_ newValue: T)
}

class Store<T> {
    private var observers = [WeakBox]()
    var value: T { didSet { notify(value) } }

    init(_ value: T) { self.value = value }

    func subscribe(_ observer: StoreObserver) {
        observers.append(WeakBox(observer))
    }

    private func notify(_ newValue: T) {
        observers.forEach { $0.value?.storeDidUpdate(newValue) }
    }

    private class WeakBox {
        weak var value: StoreObserver?
        init(_ v: StoreObserver) { value = v }
    }
}

class CounterLabel: StoreObserver {
    func storeDidUpdate<T>(_ newValue: T) {
        print("Label updated: \(newValue)")
    }
}

let store = Store(0)
let label = CounterLabel()
store.subscribe(label)
store.value = 5   // "Label updated: 5"
store.value = 10  // "Label updated: 10"