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 Notification
s 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 usemyNotification
anywhere aNotification.Name
is expected, i.e., theNotificationCenter.addObserver
andNotificationCenter.post
functions.- We chose to have a separate
func
forcatchNotification
here 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?