Notification in Swift (NSNotification)

Image for post
Image for post

In Swift, communication between entities in the app can be implemented in a few ways: Notification (NSNotification), Key-Value Observing (KVO), delegation, etc. In series of articles I will share how to select proper tool for a job and some best practices. This one is about Notification.

Notifications are often compared to delegates. You can find out more in my article Delegate in Swift.

Notification and Notification Center

object is a container for information related to a change or an event. Notifications are broadcasted using centralized instance of .

object, identified by , may contain reference to the that has sent the notification, and some arbitrary information in the dictionary.

registers observers and delivers notifications. Objects use instance to post and observe a notification. Each app (process) has a default notification center, accessible via property.

Note, on macOS can be used to communicate between processes.

For demo purpose I created the sample class that stores an array of objects.

Creating Notification

To create a new notification we need a name. Names use the nested type.

Under the hood names are actually strings, therefore it is a good practice to come up with unique names. Best practice is to declare a constant for the name. I prefer using name of the constant as a string, same convenience used in Cocoa/Cocoa Touch.

is the name of the notification posted when change occurs. It is declared using extension on . Benefit from adopting this approach is that compiler will warn you when trying to create a duplicate name.

When naming the notification include object name and meaningful description of the change. Use auxiliary verbs Will and Did to indicate if notification posted before or after the change. When not sure, look into existing names, just type and browse over autocompletion results.

Note on Swift 4 and Swift 4.2 differences

Cocoa Touch in Swift 4 uses global namespace to declare notification name constants:

In Swift 4.2 constants are nested with type:

String value stays the same - . However, the way we refer to it changes:

In Swift 4.2 you must use syntax. This makes it more structured, but we loose short syntax and ability to introspect all notifications on the type.

Posting Notification

Most convenient way to post a notification is usingconvenience method.

You rarely need to create objects yourself. Instead provide notification center with the name, posting object, and user info dictionary.

notifies when new url was added. dictionary contains the new url under key. Good practice is to declare constants for dictionary keys.

Typically argument is the object that posts the notification. In special cases it may be . Examples are system notifications like clock or time zone change. The best practice make sure it is the object that posts the notification is to always pass . This way notifications will only be posted from one of the object methods and not external code.

Observing Notification using Selector

One way to observe notifications is to add the observer and its selector to .

expects the name of the notification and the object whose notifications the observer wants to receive.

This approach is similar to the target-action pattern you see in UI controls.

In this example object responds to the notification with method.

The observer method must have one argument (an instance of ). Method without arguments still works. But this is rather a side effect and documentation explicitly requires to follow the signature.

Even when you’re not interested in notification object itself, but the fact of the event, instance as an argument helps to separate and search notification handling methods in code. So do keep argument in notification handling methods as a best practice.

Another useful practice to adopt is using name of the notification as the method name: - . This small trick will help you quick search for methods that respond to the notification.

When we’re no longer interested in observing the notifications, the observer can be removed from .

For this you need the observer object, name of the notification, and the object whose notifications the observer receiving.

Here, object stops responding to notification.

Note, prior to iOS 9 and macOS 10.11 we must remove observers by calling this method before objects are deallocated.

Observing Notification using Closure

provides closure API to observe notifications.

Like with previous method, expects the name of the notification and the object whose notifications the observer wants to receive. But this time, closure reacts to the notification, and the object that acts as the observer is returned from the method.

In this example responding to is done using closure and object stores the observer instance.

You must remove observers by invoking or before objects are deallocated.

The closure is retained by the notification center until the observer registration is removed. Naturally the closure is escaping. This may create a strong reference cycle.

In my example the observer is removed in method, but with strong reference cycle it will never happen. I use to break strong reference cycle.

General rule of thumb with weak references in closures is to capture strong reference before the closure routine. This is important because the weak reference can become at any moment. This may lead to undefined behaviour and unexpected side effects. I use next pattern for convenience:

Multiple Subscriptions

Multiple subscriptions will trigger the responding selector multiple times. But when unsubscribing — all observer selectors will be removed.

In this example will be triggered two times for the first URL and will not fire for the second URL.

Probably not what we expect. Usually we need to unsubscribe from the previous object before subscribing to the new one. This is especially simple when object is stored as a property.

Using (Property Observers) with helper methods.

Therefore, another good practice is to create separate methods for subscribing and unsubscribing to notifications.

When using closure we need to take extra care. Because if we overwrite previous observer — it can never be removed. But it may actually be simpler to control.

Here we validate if previous observer was removed before adding new one. Assertion will help us to catch this bugs during testing.

Bulk Subscriptions

One of the interesting feature of is ability to subscribe for all notifications from a certain object; for a certain notification from all objects; or basically for all notifications in the notification center.

All you need to do is to pass nil as one of the arguments. Same works for selector based subscriptions. This is useful when you don’t have a reference to the object posting the notification, when all notifications can be handled by one closure/method, or when using local notification center for certain parts of the app.

Same works for unsubscribing from notifications.

Use this methods wisely. In general it is better to explicitly enumerate all notifications you’re interested in observing and specific objects. Otherwise you may end up with undefined behavior and side-effects.

When removing observer make sure you remove only notifications you explicitly subscribed to. Especially if you use inheritance and subclass/superclass can subscribe for notifications.

Notifications and Multithreading

When observing the notification that may occur on the secondary thread it is important to respond on the thread we expect. One example is updating UI when responding to model change that may happen in background.

Notification center deliver notifications on the thread in which the notification was posted. Redirecting notifications is possible and described in documentation. But the implementation is rather complicated and limited in several aspects.

When observing notification using closure we can specify for the closure.

Using with selectors is another simple solution for the complex problem.

Notification Delivery

When using 's , the notification will be distributed synchronously. Notification center uses a queue (), that maintains notifications in a First In First Out (FIFO) order.

In this example notifications appear synchronously and in order.

This as well means, that before the posting object can resume its thread, it must wait until the notification center dispatches the notification to all observers.

Image for post
Image for post
Stack trace for URLContainerDidAddURL notification

As you can see from this stack trace, is executed synchronously.

But… notifications placed into the queue can be delayed until the end of the current pass through the run loop or until the run loop is idle. Notifications delivery can be changed using posting style () and can be performed asynchronously.

Notification and Architecture

Observation patterns are used in various architectures. Notifications are commonly used as a communication protocol in Model-View-Controller (MVC) architecture.

Image for post
Image for post
MVC design pattern diagram from developer.apple.com

Notification and UIViewController

In this example updates UI as the response to the notification. Because UI must be updated on main queue, used when adding observer. Reference to is weak and converted to strong inside the closure.

There are still some things to consider. Handling notification may be costly operation. Updating user interface, formatting values, etc. Maybe you need to download an image from the server. For the best performance it is better to avoid unnecessary work.

One option is to subscribe on appearance and unsubscribe when view disappears. This will remove unnecessary updates. But, you still need to update UI on appearance. I prefer to keep track of needed update:

In this example property to indicate when UI must be updated on appearance (in ). When notification occur, we check if view is visible (by checking if it is in the window). If view is not visible, we set property.

A bit more work, but performance gain can be huge.

Final Notes

Notification is easy to use and powerful mechanism. It works well in Model-View-Controller architecture and can be used to respond to system events.

With great power comes great responsibility.

Notification may look as a simple solution to complicated problems. But overuse of notifications may lead to complex dependencies between objects in the app, sometimes even separate modules.

When considering adding an observer, check if you have reference to the object that posts the notification. If the object is not accessible, instead of observing every notification, it might be better to solve the problem by adjusting your architecture.

When constructing data flows consider dependency injection and delegation as alternative to notifications.

In many cases, when notification looks like the only solution for the problem — it is a symptom of more complex, architectural issue. Notification must be the best solution from a selection.

You can find documentation Notifications on Apple developer website under Notification Programming Topics.

Thank you for reading. If you like the article, please follow me for future posts.

Related topic: Delegate in Swift.

Show authors more ❤️ with 👏’s

And hope to see you next time 😊

Written by

iOS Developer, here to share best practices learned through my experience. You can find me on Twitter: https://twitter.com/dmytroanokhin

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store