iAchieved.it

Software Development Tips and Tricks

By

NSNotifications with userInfo in Swift

Update 9/18/16: Swift 3.0 has brough even more changes to working with notifications and userInfo; come visit our new article on handling Notifications with Swift 3.0

Update: Swift 2.2 has brought a number of changes; come visit our new article on handling NSNotification userInfo in Swift.

I frequently sit down intent on writing about one topic, only finding myself in a situation where I feel motivated to write about something else. This is one such post.

This morning I started with working on a HomeKit application, only to take a detour to working with NSNotifications in Swift. I’m going to make the assumption that you are already familiar with both NSNotification and NSNotificationCenter. I tend to use these a lot when programming applications that have a number of moving parts and threads. I may be doing it all wrong and I’m sure there’s some pattern I’m missing out on, but the model of broadcasting events (notifications) to interested parties has served me well.

Let’s look at the basic Swift method for getting a handle to the default notification center (I’m going to break my verbosity rule here and use nc as the variable name rather than notificationCenter):

Simple enough. Now, I want to register my object (typically a view controller) for receiving a notification.

In plain English, I’m telling the notification center to call my function addHomeError whenever someone posts a kAddHomeError notification. self is the object to call and addHomeError is the function. Notice that the Objective-C @selector() construct is gone in favor of naming the function as a string. And of course kAddHomeError is simply a constant string defined somewhere else. In this case the definition is let kAddHomeError = "AddHomeError".

In another object I can post my notification with:

Since my first object registered to receive a notification when kAddHomeError was posted, its addHomeError function will be called.

In both cases I’ve left the object parameter nil. This parameter is applicable only if you want to distinguish between separate objects registering and posting notifications. When specifying nil in the addObserver function you are saying, “Hey, I don’t care who posts this notification, send it to me.” If you provide an object to the object parameter you are making a statement, “I’m only interested in notifications from this specific object.”

In the above example our function we want called is addHomeError, so we need to write it:

Given that I’m calling presentViewController in our function, it’s a safe bet to assume that I’m handling receipt of the notification in a view controller. So our notification comes from one object, and is handled in a view controller. Straightforward. What I don’t like though is the error message is Unable to add home. It would be nicer if the dialog indicated the name of the home it couldn’t add.

We can accomplish that through the use of the userInfo parameter when calling postNotificationName. It took a minute to figure out the function signature, because quite frankly it looks a little, well, non-obvious. Let’s take a look.

Perhaps you’re a quicker study than I am, but… [NSObject : AnyObject]?. Huh? What? (No pun intended.)

A glance back at the Objective-C signature for postNotificationName and the lightbulb went off. Or at least started glowing a little. The userInfo parameter in Objective-C is an NSDictionary and it stands to reason Swift would be no different. And it isn’t. The signature syntax just looks strange. Seriously though, all you need to do is pass in a dictionary where the keys are derived from NSObject and the values are AnyObject. The question mark is just there to signify that this parameter could be nil.

Unlike earlier versions of Objective-C ([NSDictionary dictionaryWithObjectsAndKeys:] anyone?), Swift comes out of the box with so-called “literal dictionaries,” a construct that other languages such as Python and Ruby call just dictionaries. So we don’t have to do any special dictionary-building to use userInfo. Our error notification can now be transformed into:

We don’t have to limit ourself to a single-entry dictionary either:

Now, let’s handle our notification. The first thing we need to do is update our function to be passed the notification object which contains our userInfo. Recall the first version of the function had no arguments, that is, we didn’t care about the NSNotification object itself.

Our method signature becomes func addHomeError(notification:NSNotification). If we made this change now and ran our application it would most surely crash. Why? Because of this right here:
nc.addObserver(self, selector: "addHomeError", name: kAddHomeError, object: nil)
. That selector definition indicates a function named addHomeError which takes no arguments. We don’t have that anymore, we take one argument. So you’re in for a unrecognized selector sent to instance crash if you try that. Update the selector to addHomeError::

Finally, in our addHomeError function, let’s unwrap our userInfo dictionary and message.

Now perhaps there’s an easier way to do this, but, when receiving the userInfo, the routine handling the notification doesn’t know what type of dictionary is there. Recall that the only requirement was that it be of type [NSObject : AnyObject]?. So our code has to be a bit more explicit as what we know the dictionary to be, and that is of type [String : String!]. Once we’ve set the compiler straight, we obtain our messageString and supply it to our UIAlertController.

Now we get a nice error message:

image1

With that out of the way I can get back to our upcoming article on HomeKit.

9 Responses to NSNotifications with userInfo in Swift

  1. Yusuf says:

    Thank you for sharing the customizable notification example. Its always so easy to learn by example.

  2. dimpiax says:

    btw, user info receive strong variables, and if you want to put value to string key, for example, you need to set value with exclamation mark.

    let value = "42"
    NSNotificationCenter.defaultCenter().postNotificationName("SomeNotificationName", object: nil, userInfo: ["mainAnswer":value!])

  3. Alex says:

    Very nice! Helped me a lot – thank you!

    Question: How did you learn Swift and Xcode? I’m currently doing a “build my own app so Google stuff” crash course, but wondered if you could recommend a better way.

  4. Thank you so much! The part that was killing me was that you need the colon after the selector method name if you have parameters. If anyone else has the same issue, I hope this comment helps!

  5. john brohan says:

    Super. Solved my issues perfectly. What’s the theory behind the “:” on the name?

  6. DICE says:

    Hi. I’m not sure if this is relevant to the post, but I can’t find elsewhere that would answer our question. For our app, we would like the user to be able to input information. Can we do that using the userInfo displayed here?

Leave a Reply

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