Looking for help using notifications with Swift 3.0? Head on over here where we go over the latest Swift 3.0 changes.
When I sit down to blog I never know what will be a popular topic and what won’t. One that definitely surprised me was how popular the original NSNotifications with userInfo in Swift would end up being. Much has changed since Swift first hit the scenes, so I thought we’d provide an update to handling NSNotification
userInfo
and some working code to help out.
The technique for obtaining the default NSNotificationCenter
has remained unchanged, and can be done with let nc = NSNotificationCenter.defaultCenter()
. What has changed in the latest versions of Swift is how to specify the selector (i.e., the function that should be called when a notification has posted). Rather than using a bare string like "catchNotification"
, Xcode will instruct you to use the #selector
directive, like this:
1 2 3 4 5 |
let nc = NSNotificationCenter.defaultCenter() nc.addObserver(self, selector: #selector(ViewController.catchNotification), name: "MyNotification", object: nil) |
In this example we’re instructing the notification center to deliver MyNotification
notifications to the catchNotification
function of the ViewController
class.
Post It!
Now, let’s look at posting (sending) a notification:
1 2 3 4 |
let nc = NSNotificationCenter.defaultCenter() nc.postNotificationName("MyNotification", object: nil, userInfo: ["message":"Hello there!", "date":NSDate()]) |
The userInfo
still takes [NSObject : AnyObject]?
as an argument, which we provide as a dictionary literal in Swift. Note that the userInfo
values don’t need to be homogeneous (that’s where the AnyObject
comes in); we are sending along a String
and an NSDate
.
Handling Notifications
The guard
construct did not exist in Swift when I wrote the original code used to pull apart the userInfo
data, but it serves as a good method to unwrap and verify that the expected data is in the userInfo
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func catchNotification(notification:NSNotification) -> Void { print("Catch notification") guard let userInfo = notification.userInfo, let message = userInfo["message"] as? String, let date = userInfo["date"] as? NSDate else { print("No userInfo found in notification") return } let alert = UIAlertController(title: "Notification!", message:"\(message) received at \(date)", preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)) self.presentViewController(alert, animated: true, completion: nil) } |
To verify that the guard
works properly switch out the NSDate()
in the call to postNotificationName
with a String
or some other object. You should see No userInfo found in notification
printed to the console.
Example Source
You can try out the code above with a simple iOS project. Create a new Single View Application and replace the contents of ViewController.swift
with the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let nc = NSNotificationCenter.defaultCenter() nc.addObserver(self, selector: #selector(ViewController.catchNotification), name: "MyNotification", object: nil) } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let nc = NSNotificationCenter.defaultCenter() nc.postNotificationName("MyNotification", object: nil, userInfo: ["message":"Hello there!", "date":NSDate()]) } func catchNotification(notification:NSNotification) -> Void { print("Catch notification") guard let userInfo = notification.userInfo, let message = userInfo["message"] as? String, let date = userInfo["date"] as? NSDate else { print("No userInfo found in notification") return } let alert = UIAlertController(title: "Notification!", message:"\(message) received at \(date)", preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)) self.presentViewController(alert, animated: true, completion: nil) } } |
Thanks for this Joe. I was having some serious issues trying to properly handle receiving remote notifications and taking the user to a specific area within the application once they tap them. This tutorial has made for a MUCH simpler implementation of what I was trying to do.
Cheers!