- Published on
Notifications and userInfo with Swift 3.0
Original article: http://dev.iachieved.it/iachievedit/notifications-and-userinfo-with-swift-3-0/
Original author: Joe
Notifications and userInfo with Swift 3.0
Swift 3.0 brought quite a few changes to the Swift language, including the removal of NS prefixes from Foundation framework types — the Great Renaming. NSThread became Thread, NSData became Data, and so on.
This means the usage of NSNotificationCenter — oh sorry, NotificationCenter — with userInfo needs to be updated. This is a significant difference between Swift 2 and Swift 3.
The way to get the default NotificationCenter is now let nc = NotificationCenter.default. Additionally, the model of using a selector when receiving notifications has changed to specifying a closure or function to execute.
For example, in Swift 2 you would write:
let nc = NSNotificationCenter.defaultCenter()
nc.addObserver(self,
selector: #selector(ViewController.catchNotification),
name: "MyNotification",
object: nil)
While in Swift 3 it's written as:
let nc = NotificationCenter.default // Note: default is a property, not a method call
nc.addObserver(forName:Notification.Name(rawValue:"MyNotification"),
object:nil, queue:nil,
using:catchNotification)
The example above sets up the notification center to deliver MyNotification notifications to the catchNotification function, which has a method signature of (Notification) -> Void. Alternatively, you can use a closure:
let nc = NotificationCenter.default // Note: default is a property, not a method call
nc.addObserver(forName:Notification.Name(rawValue:"MyNotification"),
object:nil, queue:nil) {
notification in
// Handle the notification
}
Posting Notifications
Now let's look at how to post notifications. The postNotificationName method from Swift 2.0 has been replaced by post in Swift 3.0.
let nc = NotificationCenter.default
nc.post(name:Notification.Name(rawValue:"MyNotification"),
object: nil,
userInfo: ["message":"Hello there!", "date":Date()])
userInfo takes [AnyHashable: Any]? as a parameter, which is called a dictionary literal in Swift. Note that the values in userInfo don't need to be of the same type (as indicated by the Any); here a String type and a Date type are sent together.
Handling Notifications
Using guard syntax to unwrap and validate expected data from userInfo is a great approach.
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 the guard behavior, try calling post using a String type or another object type instead of Date(). You'll see No userInfo found in notification in the console output.
Sample Code
You can try the code above in a simple iOS project. Create a Single View Application project and replace the contents of ViewController.swift with the following:
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)
}
}
Key points:
- A
Notification's "name" is no longer aStringtype but aNotification.Nametype, so when declaring a notification, uselet myNotification = Notification.Name(rawValue:"MyNotification"). This allows us to usemyNotificationanywhere aNotification.Nameis needed, such as inNotificationCenter.addObserverandNotificationCenter.post. - It is recommended to use a separate
catchNotificationmethod rather than tangled-up code blocks.
That's it — clean and effective!
Comments Section
Improved way of declaring and using notifications:
- First declare the notification name:
public extension Notification {
public class MyApp {
public static let MyNotification = Notification.Name("Notification.MyApp.MyNotification")
}
}
- Post a notification using the notification name:
NotificationCenter.default.post(name: Notification.MyApp.MyNotification, object: self)
- Observe the notification:
NotificationCenter.default.addObserver(forName: Notification.MyApp.MyNotification, object: nil, queue: OperationQueue.main) {
pNotification in
// Your code here
}