Swift: NotificationCenter Protocol

Observer pattern made better-er

NotificationCenter existed since the first release of OSX which is at least 17 years, and has been a popular tool for Apple developers ever since. For those who dont know, it’s based off the Observer pattern concept which is part of the behavioural group of software design patterns.

Observer pattern

The Observer pattern has existed ever since the Gang of Four wrote about it in the mid 90’s and is a relatively simple pattern to understand. Basically we have an object which we refer to as the subject; this subject maintains a list of dependencies otherwise known as observers, and notifies them of changes in state.

A real world example would be a busy coffee shop in your local city. Customers would line up followed by ordering a coffee, the barista would ask the customer for their name and write it on the cup so they know who it belongs to; the customer is then asked to politely wait until their name is called. As each coffee is made, the barista will call out the name written on the cup to the waiting customers so they are able to receive the correct coffee and be on their merry way.

In this scenario, the barista was the subject, the customer buying the coffee was the observer and the coffee was the state change, because the coffee went from an empty cup to a cup full of caffeinated goodness.

Problems with NotificationCenter

The Observer pattern is definitely a great pattern and has many great uses when we’re writing code. But I have to admit, I’ve never been a big fan of it, and not for lack of a few good reasons either:

Maintaining uniformity amongst subjects

Without forced standards, a project can have multiple ways of implementing and sending notifications to observers. One example is mixed notification names:

class Barista {
let notification = "coffeeMadeNotification"
}
class Trainee {
let coffeeMadeNotificationName = "Coffee Made"
}

Avoiding notification name clashes

If the developers aren’t mindful of the names they give to notifications, two different subjects may have the same notification name, and when either of them post a notification with that name an unintended observer may receive it.

Imagine if our coffee shop had two baristas making coffee, if each barista uses the same notification name, a customer could react to an unintentional notification or worse, receive a soy chai and vanilla decaffeinated latte instead of a caffe latte.

class Barista {
static let coffeeMadeNotification = "coffeeMadeNotification"
}
class Trainee : Barista { }
...
NotificationCenter.default.
.postNotificationName(Trainee.coffeeMadeNotification)

Notifications use strings as names

I hate using things that are stringly typed and you should too, they just create things that are prone to error. Don’t ever trust people’s capability of avoiding typos or programming without autocomplete, ever.

NSNotificationCenter.defaultCenter()
.postNotificationName("coffeeMadNotfication")

Alternatives

A lot of the time I would use the Delegate pattern wherever possible instead of Observer. It’s a lot like the Observer pattern but instead of a one-to-none/many communication relationship, the Delegate pattern is a one-to-none/one, which in my eyes has always been safer because it avoids the problems I’ve listed above whilst also having a few problems and limitiations of its own, but we wont get into those today.

Notifier protocol

protocol Notifier { }

We can design a protocol to solve all the problems listed above, so let’s go through each of them together and implement a Swift-ier, uniformed variation for NSNotificationCenter implementation.

Maintaining uniformity amongst subjects

Protocols are great because they force behaviour upon anything that chooses to conform to it. So for our protocol, we’re gonna give it an associatedtype:

protocol Notifier {
associatedtype Notification: RawRepresentable
}

From now on, if a class or struct in our project wants to post notifications it should conform to the Notifier protocol and supply an associated type which conforms to the RawRepresentable protocol.

class Barista : Notifier {
enum Notification : String {
case makingCoffee
case coffeeMade
}
}

Because enums are able conform to RawRepresentable in Swift, we use an enum of type String and name our notifications accordingly.

let coffeeMade = Barista.Notification.coffeeMade.rawValue
NSNotificationCenter.defaultCenter()
.postNotificationName(coffeeMade)

Avoiding notification name clashes

Once again, enums are playing a big part in this because they constrict us from writing duplicate cases. The compiler would show an error if we had created more than one case of makingCoffee. However this doesn’t solve the problem of having different classes or structs having the same cases.

let baristaNotification = Barista.Notification.coffeeMade.rawValue
let traineeNotification = Trainee.Notification.coffeeMade.rawValue
// baristaNotification: coffeeMade
// traineeNotification: coffeeMade

As you can see, we need to create a unique namespace for these notifications so there aren’t any collisions between our notification names. A great solution is to use the conforming object’s name because the compiler won’t allow classes or structs to share the same name.

let baristaNotification = 
"\(Barista).\(Barista.Notification.coffeeMade.rawValue)"
let traineeNotification =
"\(Trainee).\(Trainee.Notification.coffeeMade.rawValue)"
// baristaNotification: Barista.coffeeMade
// traineeNotification: Trainee.coffeeMade

Awesome! But now we’ve arrived at a point in our implementation where we have a catch-22 sitation. On one hand we’ve solved out namespace collision problem, but on the other our code looks like complete garbage. Yes, we have created some uniformity but it’s kind of pointless if we dont have any safeguards in place to prevent ourselves and co-developers from forgetting to include the namespace, right?

Notifier implementation

Luckily for you, I’ve taken the liberty/burden upon myself and freed us from this predicament. We’re gonna extend our protocol extension even farther and add some nice Swift API guideline-approved, class specific, candy wrappers around NSNotificationCenter function calls.

Notification name

Barista.coffeeMade

Because we expect to be using our notification namespace and name quite often, we’re going to create a function where we pass in a Notification enum and it returns a safe notification name for when we post notifications and remove observers. It’s also private because we don’t want outside code accessing this function, instead we want to force ourselves and our coworkers to conform to Notifier’s protocols for the benefits we’re about to drop on them.

Add observer

Barista.addObserver(customer, selector: .coffeeMadeNotification, notification: .coffeeMade)

From now on if we want to add an observer to a class’s list of subjects, we must now tell the class directly. In doing so our code makes a lot more sense when we’re reading and writing because we know the observer listenning specificilly for this subject’s notificaiton.

Note: If the .coffeeMadeNotfication selector argument looks strange to you, I’d suggest checking out one of my previous articles: Selector syntax sugar

Post notification

Barista.postNotification(.coffeeMade)

Ermagheeerd, I know right? Posting notifications is so much nicer now. We’ve removed the long calls made with NSNotificationCenter.defaultCenter() and included default values of nil for object and userInfo so our function calls aren’t always so long. We also know that our notifications aren’t going to clash with other classes because the conforming object’s name will be appended to the notification name.

Remove observer

Barista.removeObserver(customer, notification: .coffeeMade)

Just like the addObserver API, we have tell the class to remove an observer from it’s subject list for a Notification.

Extras

The Notifier protocol has even more functionality included with it, which takes advantages of variadic parameters for deregistering multiple notifications with a single line of code and instance functions, but for the sake of this post I’ve decided not to include them because it would detract from the original idea. All the code excluded from this article will be in the sample code at the bottom of the article.

Example code

So now that we have our NSNotificationCenter wrappers inside our extension of Notifier, we’ve removed the problem of our co-developers possibly forgetting to attach the name space and we’ve made our code look much much sweeter. Don’t believe me? Check it out for yourself:

We’ve removed all the ambiguities usually associated with using NSNotificationCenter by making the subject objects responsible for their observer list. So from now on, if an observer wants to listen or stop listenning for a notification it has to inform the subject to modify it’s observer list.


As always I’ve provided a playgrounds on GitHub for you to check out as well as a Gist incase you aren’t in front of Xcode.

If you like what you’ve read today you can check our my other articles or want to get in touch, please send me a tweet or follow me on Twitter, it really makes my day. I also organise Playgrounds Conference in Melbourne, Australia and would to see you at the next event.