The Observer Design Pattern in Swift
Tags: webdev, programming, javascript, design
Published: 2023-03-08 17:31:47

---
title: The Observer Design Pattern in Swift
published: true
description: A beginner's guide to design patterns for iOS developers
tags: webdev, programming, javascript, design
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zad6s1g4qxna9mclzxh1.jpg
canonical_url: https://kaww.hashnode.dev/the-observer-design-pattern-in-swift
# Use a ratio of 100:42 for best results.
# published_at: 2023-03-05 17:58 +0000
---

In this article, we will learn what is the Observer design pattern and how to use it.

## The use case

Let’s say we have a weather station that regularly takes measures of temperature and humidity:

```swift
struct WeatherData: Equatable {
    var temperature: Int
    var humidity: Int
}

class WeatherStation {

    private var timer = Timer()
    private var lastMeasurement: WeatherData? = nil

    init() {
        self.timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { [weak self] _ in
            self?.measureWeather()
        })
    }

    private func measureWeather() {
        // Simulate new data
        let newData = WeatherData(temperature: Int.random(in: 0..<10), humidity: Int.random(in: 0..<10))
        if newData != lastMeasurement {
            self.lastMeasurement = newData
        }
    }
}
```

And two screens that are connected to the station, one displaying temperature, the other one displaying humidity:

```swift
struct TemperatureScreen {
    func display(temperature: Int) {
        print("Temperature is now \(temperature)")
    }
}

struct HumidityScreen {
    func display(humidity: Int) {
        print("Humidity is now \(humidity)")
    }
}
```

→ How would we allow the several screens to update their information when the weather changes?

## Polling the data?

One solution that can come to mind would be that a screen can regularly ask the weather station for new data, for instance, every 100 milliseconds.

Unfortunately, this won’t be very efficient. Here is why:

- If the weather doesn’t change during a minute, all screens would ask a lot of times the weather station for nothing, wasting energy.
- The more screens you have, the more the weather station will receive simultaneous requests, which risks to overload the station.

So instead of making the screens asking the new data, let’s try to see the solution in the other direction: what if it’s the weather station's responsibility to inform the screens of new data?

## The observer pattern

The observer pattern is based on two elements:

- The observable (also called the subject): the object that is observed, the weather station.
- The observers (also called subscribers): they observe the observable. The screens observe the weather station.

The responsibility of the observable is to keep a list of its observers and notify them if needed. Then, the observers can register or unregister themselves from the observable.

We can see that like a newsletter: a person can subscribe to a newsletter, and the newsletter system will notify each subscriber when new information is available.

Let’s see what the Swift code would look like.

## The code

First, we have to define the two protocols: the observable and the observer

### Observable

The observable is composed of:

1. A list of subscribers, of the type `WeatherStationSubscriber` (we will come to that).
2. A `register` method, that we can call to add subscribers. An `unregister` method should also be added to stop a subscription.
3. A `notify` method that will be used to notify the subscribers.

```swift
protocol WeatherStationObservable {
    // 1
    var subscribers: [WeatherStationSubscriber] { get }
    // 2
    func register(_ subscriber:  WeatherStationSubscriber)
    // 3
    func notify(_ newData: WeatherData)
}
```

Let’s apply that to our weather station:

1. We add the `WeatherStationObservable` protocol conformance to our `WeatherStation`.
2. We declare the list of subscribers.
3. We implement the `register` method, by adding the new subscriber to the list of subscribers. Note that you may want to prevent the same observer to subscribe multiple times, by checking before if the subscriber is already in the `subscribers`, or by using a Set instead of a list.
4. Finally, we implement the `notify` method: we just have to iterate through each of the subscribers and notify them with the new data.

```swift
class WeatherStation: WeatherStationObservable { // 1

    // previous code ...

    // 2
    var subscribers: [WeatherStationSubscriber] = []

    // 3
    func register(_ subscriber: WeatherStationSubscriber) {
        subscribers.append(subscriber)
    }

    // 4
    func notify(_ newData: WeatherData) {
        for subscriber in subscribers {
            // TODO: notify subscriber here !
        }
    }
}
```

Now we just have to modify the `measureWeather` method to call the `notify` method:


```swift
private func measureWeather() {
    // Simulate new data
    let newData = WeatherData(temperature: Int.random(in: 0..<10), humidity: Int.random(in: 0..<10))
    if newData != lastMeasurement {
        self.lastMeasurement = newData
		self.notify(newData)
    }
}
```

### Observer

The observer (here we call it subscriber) is even easier: it just has to implement a method that can be called by the subscriber to notify it when new data is available:

```swift
protocol WeatherStationSubscriber {
    func onNotified(_ newData: WeatherData)
}
```

Let’s apply that to one of our screens:

- Here the `onNotified` method will just send the temperature information to the display method.

```swift
struct TemperatureScreen: WeatherStationSubscriber {

	// Previous code...

	func onNotified(_ newData: WeatherData) {
        display(temperature: newData.temperature)
    }
}
```

### Usage

Now let’s see how to use that in practice:

1. We create the weather station.
2. We create the screens.
3. We register the screens to the weather station.

```swift
// 1
let weatherStation = WeatherStation()

// 2
let temperatureScreen = TemperatureScreen()
let humidityScreen = HumidityScreen()

// 3
weatherStation.register(temperatureScreen)
weatherStation.register(humidityScreen)
```

## The final code

```swift
import Foundation

struct WeatherData: Equatable {
    var temperature: Int
    var humidity: Int
}

protocol WeatherStationObservable {
    var subscribers: [WeatherStationSubscriber] { get }

    func register(_ subscriber:  WeatherStationSubscriber)
    func notify(_ newData: WeatherData)
}

protocol WeatherStationSubscriber {
    func onNotified(_ newData: WeatherData)
}

class WeatherStation: WeatherStationObservable {

    private var timer = Timer()
    private var lastMeasurement: WeatherData? = nil

    var subscribers: [WeatherStationSubscriber] = []

    init() {
        self.timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { [weak self] _ in
            self?.measureWeather()
        })
    }

    private func measureWeather() {
        print("Measuring...")
        let newData = WeatherData(temperature: Int.random(in: 0..<10), humidity: Int.random(in: 0..<10))
        if newData != lastMeasurement {
            self.lastMeasurement = newData
            notify(newData)
        }
    }

    func register(_ subscriber: WeatherStationSubscriber) {
        subscribers.append(subscriber)
    }

    func notify(_ newData: WeatherData) {
        for subscriber in subscribers {
            subscriber.onNotified(newData)
        }
    }
}

struct TemperatureScreen: WeatherStationSubscriber {
    func onNotified(_ newData: WeatherData) {
        display(temperature: newData.temperature)
    }

    func display(temperature: Int) {
        print("Temperature is now \(temperature)")
    }
}

struct HumidityScreen: WeatherStationSubscriber {
    func onNotified(_ newData: WeatherData) {
        display(humidity: newData.humidity)
    }

    func display(humidity: Int) {
        print("Humidity is now \(humidity)")
    }
}

let weatherStation = WeatherStation()

let temperatureScreen = TemperatureScreen()
let humidityScreen = HumidityScreen()

weatherStation.register(temperatureScreen)
weatherStation.register(humidityScreen)
```

Now if we run that code in a playground, we can see each time the weather station gets new data, it will notify the screens that will display the new weather information:

```
Measuring...
Temperature is now 7
Humidity is now 3

Measuring...
Temperature is now 1
Humidity is now 9

Measuring...
Temperature is now 4
Humidity is now 8
```

## Wrap up

In this article, we learned what is the observer design pattern and how to use it.

*I hope this article has been helpful to you. If you have any questions or feedback about this article, don’t hesitate to contact me on* ***[Twitter](https://twitter.com/kaww)**!*