NSNotificationCenter in Swift 4: Intra-app communication, sending, receiving, listening, stop listening for messages

[Download Xcode 9 project with full Swift 4 source from GitHub.]

The topic today is how to communicate between objects within an app, specifically using iOS’s NSNotificationCenter. In the last post, “Tutorial: delegates and delegation in Swift 4,” I used delegation to communicate between two objects. In the code I wrote for the last article, the app’s main (and only) view controller waited to display an image. The image was not included in the app bundle, rather downloaded from a NASA website. I created a class that downloaded the image file. The view controller was informed that the image was finished downloading and ready to display using a technique called “delegation.” Here, we’ll modify the delegation tutorial code to work with NSNotificationCenter instead. The image downloading class will notify the view controller that the image has finished downloading by sending a notification (message).

I will show you: 1) a one-post-to-one-observer notification; and, 2) a broadcast, where one post is received by many observers. Download the project and follow along in Xcode. Here’s the app we’ll build:


Background

In the parlance of an object-oriented programming language like Objective-C, a phrase like “calling an object method” is synonymous with the phrase “sending a message to an object instance.” This is not the kind of messages we’re talking about today.

We’re going to talk about NSNotificationCenter, which is a much more freewheeling approach to communication between aspects (objects) of an application.

A good metaphor for NSNotificationCenter is a radio station with a bunch of people tuned in and listening. The radio station applies for and gets a specific frequency, say, 104.4 FM, from the FCC. It broadcasts music and talk shows — content that people want to hear. That content is a form of message. If people out there in the radio station’s listening area want to listen to 104.4 FM, they have to tune their radios to match the FM frequency of 104.4. They’ve subscribed to 104.4. There’s one radio station in the area with frequency 104.4 FM and there are a bunch of people listening to 104.4 FM. People don’t have to listen to 104.4 FM, but they can if they want. Listeners don’t have to know any details about the radio station (like its office address, annual revenues, etc.). The radio station doesn’t have to know the blood type and refrigerator make of each listener (though with today’s aggressive marketing approaches, they’d probably like to know).

A developer can use NSNotificationCenter to post (send) message(s) with a unique ID and a bit of some optional extra data. These messages (NSNotifications) are sent out — broadcast — into the cosmos, and there’s no guarantee that anyone is listening. If there are listeners (observers) who have subscribed to that message with that unique ID (NSNotification.Name), those listeners will receive the message.

Yes, there can be one notifier and one observer, but here’s the kicker: there can be one notifier and many observers, just like in my radio station metaphor.

NSNotificationCenter is an example of loose coupling. The players don’t have a whole lot of interdependencies. I started this article mentioning Objective-C and one object calling a method of another object. Even with Objective-C’s late/dynamic binding, objects need to know some things about other objects when calling each other’s methods (if you want the methods to work; if they’re optional, who cares?). All’s I’m saying is that method calls are more tightly coupled than sending messages via NSNotificationCenter.

Whenever messaging technologies like NSNotificationCenter are brought up, the inevitable comparison to delegation is brought up. Developers need to know when objects should communicate via NSNotificationCenter or delegation (see my detailed tutorial here).

Here is Apple’s take on calling object methods and the differences between notifications and delegation:

The standard way to pass information between objects is message passing–in which one object invokes the method of another object. However, message passing requires that the object sending the message know who the receiver is and what messages it responds to. This requirement is true of delegation messages as well as other types of messages. At times, this tight coupling of two objects is undesirable–most notably because it would join together what might be two otherwise independent subsystems. And it is impractical because it would require hard-coded connections between many disparate objects in an application.

For cases where standard message passing just won’t do, Cocoa offers the broadcast model of notification. By using the notification mechanism, one object can keep other objects informed of what it is doing. In this sense, it is similar to delegation, but the differences are important. The key distinction between delegation and notification is that the former is a one-to-one communication path (between the delegating object and its delegate). But notification is a potentially one-to-many form of communication–it is a broadcast. An object can have only one delegate, but it can have many observers, as the recipients of notification are known. And the object doesn’t have to know what those observers are. Any object can observe an event indirectly via notification and adjust its own appearance, behavior, and state in response to the event. Notification is a powerful mechanism for attaining coordination and cohesion in a program.

You already use NSNotificationCenter
Just so you know, you’re already using notifications in your apps (the following quote applies to macOS, but the same type of thing is happening in iOS):

The delegate of most Cocoa framework classes is automatically registered as an observer of notifications posted by the delegating object. The delegate need only implement a notification method declared by the framework class to receive a particular notification message. Following the example above, a window object posts an NSWindowWillCloseNotification to observers but sends a windowShouldClose: message to its delegate.

How notifications work

Generally, from Apple’s documentation:

How the notification mechanism works is conceptually straightforward. A process has an object called a notification center, which acts as a clearing house and broadcast center for notifications. Objects that need to know about an event elsewhere in the application register with the notification center to let it know they want to be notified when that event happens. An example of this is a controller object that needs to know when a pop-up menu choice is made so it can reflect this change in the user interface. When the event does happen, the object that is handling the event posts a notification to the notification center, which then dispatches the notification to all of its observers.


Specifically, from Apple’s documentation:

Objects register with a notification center to receive notifications (NSNotification objects) using the addObserver(_:selector:name:object:) or addObserver(forName:object:queue:using:) methods. When an object adds itself as an observer, it specifies which notifications it should receive. An object may therefore call this method several times in order to register itself as an observer for several different notifications.

Each running app has a default notification center, and you can create new notification centers to organize communications in particular contexts.

… and from related documentation

Posts a given notification to the notification center…

Posting Notifications

Creates a notification with a given name and sender and posts it to the notification center.

Notification setup workflow
This is the order in which I set up messaging using NSNotificationCenter:

1) define global and unique notification identifiers for each notification to be sent/broadcast and received;
2) decide where I am going to listen/observe for notifications and what, in general, to do when I receive those notifications;
3) decide where I am going to send/post/broadcast notifications;
4) define what specifically I’ll do when I receive notifications; and,
5) stop listening for notifications — remove the observers — when I know I am done with notifications.

I know my steps may seem a little odd, but you’re probably starting to realize that I design — at least outline — my overall solution before implementing it.

The code

Think about what you just read, compare to my previous post, then read the code and inline commentary below. Download the sample code, build, and run the app. Note my comments in the code where I’ve materialized my notification setup workflow steps for messaging using NSNotificationCenter (STEP 1, STEP 2.1, STEP 2.2, STEP 3.1, STEP 3.2, STEP 4.1, STEP 4.2, and STEP 5).

LogoDownloader.swift

ViewController.swift

What did you think?

Do you understand NSNotificationCenter and messaging in iOS and Swift? Notifications are easier to wrap your brain around than delegates, but be warned, do not fall into the trap of over-using notifications. You’ll end up with the biggest tangle of spaghetti imaginable.

As always, listen to your grandfather! Practice, pratice, practice, get experience, work hard — but please play and enjoy your lives, too!

Author: Andrew Jaffee

Avid and well-published author, software engineer, designer, and developer, now specializing in iOS mobile app development in Objective-C and Swift, but with a strong background in C#, C++, .NET, JavaScript, HTML, CSS, jQuery, SQL Server, MySQL, Oracle, Agile, Test Driven Development, Git, Continuous Integration, Responsive Web Design, blah, blah, blah ... Did I miss any fad-based catch phrases? My brain avatar was kindly provided by http://icons8.com under a Creative Commons Attribution-NoDerivs 3.0 Unported license.

Leave a Reply

Your email address will not be published. Required fields are marked *