Black binocular on round device

Yeah, I know… If you are developing for iOS Apple already gives you a plethora of notification systems such as KVO (Key-Value Observing), delegation and the NotificationCenter.

But what if, just out of curiosity, you wanted to build your notification system? How would one achieve that? Well, there is a design pattern called the observer pattern that is described in the Gang of Four book.

With this pattern, objects can easily be notified when certain events happen without tightly coupling the subject (the object that is being observed) and the observers (those who will receive the notification).

First, let’s build a Subscription class. This class receives an event handler and an object of type Event (we will define it later) in its initializer and handles the notification to one single observer. The Subscription class will conform to 2 protocols: Notifier (making it able to notify an object) and Destroyable (if we don’t need to notify an observer anymore we can just destroy the subscription).

protocol Notifier: class {
    func notify(_ data: Any)
}

protocol Destroyable {
    func destroy()
}

class Subscription<T>: Notifier, Destroyable {
    let handler: (T) -> ()
    let event: Event<T>

    init(handler: @escaping (T) -> (),
         event: Event<T>) {
        self.event = event
        self.handler = handler
    }

    func notify(_ data: Any) {
        if let data = data as? T {
            handler(data)
        }
    }

    func destroy() {
        event.eventHandlers = event.eventHandlers.filter { $0 as AnyObject? !== self}
    }
}

After it, let’s define the Event. The Event represents what’s happening that you want to notify, e.g. you connected (or not) to a Bluetooth device or your view loaded (or not). Noticed those “or not”s? In the event object, we would like to pass some data that would indicate the state of the event. It would also be nice that all these notifications happened in different threads, that way the processing of one notification would not affect the other. Let’s code!

class Event<T> {
    typealias EventHandler = (T) -> ()
    var eventHandlers = [Notifier]()

    func notifyObservers(_ data: T) {
        eventHandlers.forEach { (eventHandler) in
            DispatchQueue.global(qos: .userInitiated).async { 
                eventHandler.notify(data)
            }
        }
    }

    func removeObservers() {
        eventHandlers.removeAll()
    }

    func addHandler(_ handler: @escaping EventHandler) -> Destroyable {
        let subscription = Subscription(handler: handler, event: self)
        eventHandlers.append(subscription)
        return subscription
    }
}

So now let’s see how to use it. In this code example, we are getting notified and receiving the timestamp of arrival when a parcel is delivered by a mailman to a house. Notice that once we destroy the subscription we are not notified anymore.

class Mailman {
    func deliverMailTo(_ house: House) {
        house.receiveMail()
    }
}

class House {
    let mailArrived = Event<String>()
    var address = ""

    init(address: String) {
        self.address = address
    }

    func receiveMail() {
        let date = Date()
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss dd.MM.yyyy"
        let dateString = formatter.string(from: date)
        mailArrived.notifyObservers(dateString)
    }
}

func main() {
    let house = House(address: "123 Tardis Ave")
    let subscription = house.mailArrived.addHandler({ date in 
        print("Mail arrived in \(house.address) at \(date)")
    })

    let mailman = Mailman()

    mailman.deliverMailTo(house)

    subscription.destroy()

    mailman.deliverMailTo(house)
}

That’s it! The full code for this demo is here. If you want to see other implementations of the observer pattern you can check out the Wikipedia page on it.