Editor’s Note: This is one of our most popular posts, so I wanted to take a moment and verify that the Swift 3.0 code presented below is still accurate with Swift 4.1. I’m happy to say it still is, so enjoy posting Notifications with userInfo in Swift 4!
Swift 3.0 has brought a number of changes to the Swift language, including the Great Renaming which brought about the end of the NS prefix on Foundation classes. NSThread is now simply Thread. NSData becomes Data. You get the idea.
That means we need to provide an update on using NSNotificationCenter, sorry, NotificationCenter with userInfo. Things have definitely changed between Swift 2 and Swift 3.
The technique for obtaining the default NotificationCenter has changed, and can now be done with let nc = NotificationCenter.default. In addition the model of using selectors has changed to specifying a block or funtion to execute when the notificaiton is received.
For example, in Swift 2 we would write:
|
1 2 3 4 5 |
let nc = NSNotificationCenter.defaultCenter() nc.addObserver(self, selector: #selector(ViewController.catchNotification), name: "MyNotification", object: nil) |
whereas Swift 3 code would look like this:
|
1 2 3 4 |
let nc = NotificationCenter.default // Note that default is now a property, not a method call nc.addObserver(forName:Notification.Name(rawValue:"MyNotification"), object:nil, queue:nil, using:catchNotification) |
In this example we’re instructing the notification center to deliver MyNotification notifications to the catchNotification function which has a signature of (Notification) -> Void. Alternatively we could use a trailing closure:
|
1 2 3 4 5 6 |
let nc = NotificationCenter.default // Note that default is now a property, not a method call nc.addObserver(forName:Notification.Name(rawValue:"MyNotification"), object:nil, queue:nil) { notification in // Handle notification } |
Post It!
Now, let’s look at posting (sending) a notification. The postNotificationName method in Swift 2.0 has been replaced with post in Swift 3.0.
|
1 2 3 4 |
let nc = NotificationCenter.default nc.post(name:Notification.Name(rawValue:"MyNotification"), object: nil, userInfo: ["message":"Hello there!", "date":Date()]) |
The userInfo now takes [AnyHashable:Any]? 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 Any comes in); we are sending along a String and a Date.
Handling Notifications
The guard construct 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:Notification) -> Void { print("Catch notification") guard let userInfo = notification.userInfo, let message = userInfo["message"] as? String, let date = userInfo["date"] as? Date 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.present(alert, animated: true, completion: nil) } |
To verify that the guard works properly switch out the Date() in the call to post 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 |
import UIKit class ViewController: UIViewController { let myNotification = Notification.Name(rawValue:"MyNotification") override func viewDidLoad() { super.viewDidLoad() let nc = NotificationCenter.default nc.addObserver(forName:myNotification, object:nil, queue:nil, using:catchNotification) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let nc = NotificationCenter.default nc.post(name:myNotification, object: nil, userInfo:["message":"Hello there!", "date":Date()]) } func catchNotification(notification:Notification) -> Void { print("Catch notification") guard let userInfo = notification.userInfo, let message = userInfo["message"] as? String, let date = userInfo["date"] as? Date 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.present(alert, animated: true, completion: nil) } } |
A few notes here:
Notification“names” are no longer strings, but are of typeNotification.Name, hence why we declarelet myNotification = Notification.Name(rawValue:"MyNotification"). This allows us to usemyNotificationanywhere aNotification.Nameis expected, i.e., theNotificationCenter.addObserverandNotificationCenter.postfunctions.- We chose to have a separate
funcforcatchNotificationhere rather than utilizing a trailing closure.
And that’s it! Simple and effective.
Here an improvement how to declare and use your notification:
1) First declare your notification name:
2) Post a notification with your notification name
3) Catch your notification
Thanks for the feedback Tof! I’ve incorporated your fix into your post.
What about releasing the observer?