Observer Design Pattern in Swift

1. Definition

The Observer pattern is a behavioral design pattern where an object, called the subject, maintains a list of its dependents, called observers, and notifies them of any state changes, usually by calling one of their methods.

2. Problem Statement

Suppose you're building a weather application. Whenever the weather data changes (like temperature or humidity), multiple display elements (like current conditions or forecast displays) need to be updated. How can you make sure that when the underlying data changes, every dependent module gets updated without tightly coupling them together?

3. Solution

The Observer pattern allows you to define a one-to-many dependency between objects so that when one object changes its state, all its dependents are notified and updated automatically.

4. Real-World Use Cases

1. A news agency and its subscribers.

2. Stock market with investors.

3. GUI tools where changes in one component reflect on others.

5. Implementation Steps

1. Define the Observer protocol that declares update methods.

2. Define the Subject protocol that declares methods to attach, detach, and notify observers.

3. Implement concrete classes for the Subject and Observers.

6. Implementation in Swift Programming

// Observer protocol
protocol Observer {
    func update(temperature: Float, humidity: Float, pressure: Float)
}
// Subject protocol
protocol Subject {
    func registerObserver(_ observer: Observer)
    func removeObserver(_ observer: Observer)
    func notifyObservers()
}
// Concrete implementation of Subject
class WeatherData: Subject {
    private var observers: [Observer] = []
    private var temperature: Float = 0.0
    private var humidity: Float = 0.0
    private var pressure: Float = 0.0
    func registerObserver(_ observer: Observer) {
        observers.append(observer)
    }
    func removeObserver(_ observer: Observer) {
        if let index = observers.firstIndex(where: { $0 as AnyObject === observer as AnyObject }) {
            observers.remove(at: index)
        }
    }
    func notifyObservers() {
        for observer in observers {
            observer.update(temperature: temperature, humidity: humidity, pressure: pressure)
        }
    }
    func measurementsChanged() {
        notifyObservers()
    }
    func setMeasurements(temperature: Float, humidity: Float, pressure: Float) {
        self.temperature = temperature
        self.humidity = humidity
        self.pressure = pressure
        measurementsChanged()
    }
}
// Concrete Observer
class CurrentConditionsDisplay: Observer {
    private var temperature: Float = 0.0
    private var humidity: Float = 0.0
    func update(temperature: Float, humidity: Float, pressure: Float) {
        self.temperature = temperature
        self.humidity = humidity
        print("Current conditions: \(temperature)°F and \(humidity)% humidity")
    }
}
// Usage
let weatherData = WeatherData()
let currentDisplay = CurrentConditionsDisplay()
weatherData.registerObserver(currentDisplay)
weatherData.setMeasurements(temperature: 80, humidity: 65, pressure: 30.4)

Output:

Current conditions: 80.0°F and 65.0% humidity

Explanation:

1. The Observer protocol defines an update interface for objects that need to be notified of changes in a subject.

2. The Subject protocol allows for attaching, detaching, and notifying observers.

3. WeatherData is a concrete implementation of the Subject.

4. CurrentConditionsDisplay is a concrete observer that prints the current conditions.

5. When we set new measurements, all registered observers get notified and updated.

7. When to use?

Use the Observer pattern when:

1. An abstraction has two aspects, with one depending on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently.

2. A change to one object requires a change to others, and you don't know how many objects need to be changed.

3. An object should be able to notify other objects without making assumptions about who these objects are.


Comments