The new UserNotification
framework changes everything you need to know about notifications. One major change is you can manage notifications quickly and easily with only a few methods. Besides adding notifications This lets you monitor, delete and update already scheduled notifications.Additionally there’s a delegate which gives you in-app notifications. In this lesson we’ll explore the power of the UNUserNotificationCenter
and UNUserNotificationCenterDelegate
.
Setting Up the Project
There’s a starter file which you can download here if you wish to skip the setup. Create new iOS single view project in Xcode. Name it NotificationManagementDemo with Swift as the language and a Universal device. You see a field Display Name when the files load. Change that the User Notification Center Demo. On the storyboard add three buttons titled Set Notification, List Notifications and Remove Notification. If you don’t want to you can just leave them any where. I formatted them with a stack view and auto layout so they look like this:
With a light background(#FFF8F0), I used the Title 1 Text Style for the font. I selected all three buttons and clicked the stack view button to make a stack view. I set the stack view attributes to Axis Vertical, Alignment Fill and Distribution Fill Equally . With the stack view selected, I pinned the stack view using the 20 top 0 left, 0 right and 20 bottom, and updated frames for new constrains.
Open the assistant editor. Using control-drag from the appropriate button, add three actions to the view controller named setNotification, listNotification, and removeNotification.
@IBAction func setNotification(_ sender: UIButton) { } @IBAction func listNotification(_ sender: Any) { } @IBAction func removeNotification(_ sender: UIButton) { }
Close the assistant editor. Go to the ViewController.swift file. Add the code for a notification and to do the required check asking for permission to use notifications. I’ve covered the details of these steps in my post on iOS 10 notifications if you want more detail of what I did.
You’ll need to add the framework first. Under import UIKit
in your code, add this:
import UserNotifications
Add a property to the class as our user access flag:
var isGrantedNotificationAccess = false
Add the code to check for access in viewDidLoad
. I left the alert to ask the user to grant access after the first launch as a comment here. Usually you’d stick in an alert to tell the user this app doesn’t work unless you have notifications, but I’m keeping this simple.
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. UNUserNotificationCenter.current().requestAuthorization( options: [.alert,.sound,.badge], completionHandler: { (granted,error) in self.isGrantedNotificationAccess = granted if !granted{ //add alert to complain } }) }
Finally, add the notification to the setNotification
action.
@IBAction func setNotification(_ sender: UIButton) { <a href="https://apppie.files.wordpress.com/2016/11/notificationmanagementdemo_start.zip">notificationmanagementdemo_start</a> }
This sets up a notification that will appear 10 seconds after scheduling. If you want the details of what I am doing here, check out the local notifications in iOS 10 post. I’m going to assume you know this much. Again, if you want to save yourself some coding, you can download the startup file here.
In-App Notifications
One exciting new feature to notifications is in-app or foreground notifications. Usually your application is in the background or the phone is asleep to launch a notification. The UserNotification
framework changes this, using a UNUserNotificationCenterDelegate
method. Adopt the delegate into the view controller:
class ViewController: UIViewController,UNUserNotificationCenterDelegate {
In the last line of viewDidLoad
, add the following line to set the delegate.
UNUserNotificationCenter.current().delegate = self
You’ll implement one delegate method with the very long name of userNotificationCenter(center: willpresent notification: withCompletionHandler)
. Add it to your code.
//MARK: Delegates func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { code }
While this looks messy, it’s not as bad as you think. completionHandler
is a closure with a parameter of type UNNotificationPresentationOptions
. When you run a notification in the app that calls it, you may want different presentation behavior than one displaying outside the app. You might skip a sound and a badge for an in-app notification. You decide the behavior for an in-app notification by calling completionHandler with the presentation options you want. I’ll use a sound and alert for this app. Replace the code
tag in the method with this.
completionHandler([.alert,.sound])
Adding that small amount of code, you get a foreground notification. Build and run. You’ll get the alert from the system to allow notifications.
Select Allow. Tap the Set Notification button. Wait ten seconds. The notification appears in the app.
If you don’t set values in the completion handler, nothing appears in app — it’s only a background app. For a foreground notification, you might want to do something different than a background one. . That’s why all the parameters here. You can access the notification and use it in other code. For example, instead of showing the notification, you could show an alert. Change the code to this, commenting out the completionHandler
.
//completionHandler([.alert,.sound]) let alert = UIAlertController( title: notification.request.content.title, message: notification.request.content.body, preferredStyle: .alert) let okAction = UIAlertAction( title: "OK", style: .default, handler: nil) alert.addAction(okAction) present(alert, animated: true, completion: nil)
The title and message of the alert come from the notification.request.content
set in the notification. You see the same information, but as an alert instead of a notification. Build and run. Press the Set Notification button and you get this.
You can do this with an UIAlertController
, but you don’t want to in most cases. This gets really annoying as it interrupts the view to do it. Apple’s human interface guide recommends using alerts sparingly and only when you really need the user’s attention. I used an alert because it’s a simple example of the flexibility of the delegate method. Most often I’ll just use the simple completion handler. This gives the user the notification they know and love, and doesn’t mess up what you are doing like a UIAlertController
does. Comment out the alert and uncomment the completion handler.
completionHandler([.alert,.sound]) /* Not a very good way to do this, just here to give you ideas. let alert = UIAlertController( title: notification.request.content.title, message: notification.request.content.body, preferredStyle: .alert) let okAction = UIAlertAction( title: "OK", style: .default, handler: nil) alert.addAction(okAction) present(alert, animated: true, completion: nil) */
Request Identifiers and Monitoring
Take a look at the notification request in setNotification
:
//Create the request let request = UNNotificationRequest( identifier: "my.notification", content: content, trigger: trigger)
The identifier
parameter sets a unique identifier for this request. The UNUserNotificationCenter
uses this identifier to manage all the notification requests on the system. In setNotification
, you added the notification request to the UNUserNotificationCenter
to schedule the notification.
//Schedule the request UNUserNotificationCenter.current().add( request, withCompletionHandler: nil)
Usually you use the current notification center for scheduling by UNUserNotificationCenter.current()
. Adding is not the only method for notification management. You can get a list of notifications. Change listNotifications
to this:
@IBAction func listNotification(_ sender: Any) { UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: {requests -> () in print("\(requests.count) requests -------") for request in requests{ print(request.identifier) } }) }
The getPendingNotifications(completionHandler:)
has a closure as its parameter. (If you are not familiar with closures you might want to refer here. ) You add your code to the closure to process a collection of requests. Here I named it requests
. This code counts the requests and then lists them on the console by request identifier.
Build and run the code. Tap the Set Notification and then Tap the List Notification. You’ll get in the console the following.
1 requests------- my.notification
Watch for the notification to fire. Once it does, tap the List Notification button and the notification disappears.
0 requests-------
getPendingNotificationRequests
is for those requests which have yet to fire. Once fired it disappears from this list. There is a second list of delivered notifications, those still showing on the notification center. Its method is very close the getPendingNotificationRequests
. Add this to the listNotification code, after the getPendingNotificationRequests
.
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: {deliveredNotifications -> () in print("\(deliveredNotifications.count) Delivered notifications-------") for notification in deliveredNotifications{ print(notification.request.identifier) } })
Instead of a request, getDeliveredNotifications
works with a collection of notifications. As you see in the print
statement, you specify the request first, then the identifier as notification.request.identifier
. Build and run. Tap the Set Notification and tap the list. you get this:
1 Delivered notifications------- my.notification 1 requests ------- my.notification
The earlier notification is the delivered one. Had we dismissed it , there would have been 0 delivered. Wait for the notification then tap List Notifications and you get this:
1 Delivered notifications------- my.notification 0 requests-------
The request becomes a delivered notification. Tap Set Notification again, but this time tap the notification to dismiss it.
0 Delivered notifications------- 0 requests -------
Once you interact with the notification, it gets removed from the delivered notifications.
Updating Notifications
The request identifier is unique. If you add another request with the same identifier, you replace the request. The request resets to a more current notification. By replacing requests, you can update a notification with more current notifications.
For our demo, you’ll count the number of times the user presses the notification button. You’ll need a counter for this. Add another property to the ViewController
class.
var pressed = 0
Under the content.body = "Notifcation pressed"
assignment in setNotification
, add the following code.
pressed += 1 content.body = "Notification pressed \(pressed) times"
Build and run, Press the Set Notification button 5 times. Each time you tap the button, the notification is replaced. Wait 10 seconds after the last tap, and you get this notification:
Repeating Notifications
In the UserNotification
framework, UNTimeIntervalNotificationTrigger
triggers can repeat infinitely a notification. As notifications have a 50 notification limit per application this is a great feature for repeat notifications. Instead of taking up your allotment with a lot of notifications, you only need one. There’s one catch: the notification trigger must have a 60 second or longer time interval. Make the notification a repeating notification. Change the trigger to this and you have a repeating notification every minute:
let trigger = UNTimeIntervalNotificationTrigger( timeInterval: 60.0, repeats: true)
Deleting Notifications
There’s of course one problem with repeating notifications: stopping them. This notification would go on forever if you let it. When you need to stop a notification or remove any pending notification, use the removePendingNotificationRequests(withIdentifiers:)
method. The parameter takes a String
array of identifiers you want to delete. There is only one in this code so we’ll change the removeNotification
action to this:
@IBAction func removeNotification(_ sender: UIButton) { UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["my.notification"]) }
Build and run. Tap the Set Notification once. Wait a minute and the first notification shows. Tap to dismiss it. Wait another minute and You’ll see another notification. Press List Notification to see the delivered notification and the request. Press Delete Notification, and then List notification. The number of requests is now zero. The notification is deleted.
With the UserNotification
framework You now have the ability to add, change and delete any notification. I’ve shown you a few things you can do to manage your notifications with the UIUserNotificationCenter
class. I’ve shown you all this in local notifications for simplicity sake, but this works exactly the same with remote notifications. That’s one of the biggest advantages of the UserNotification
framework: it works the same in iOS, watchOS, and tvOS for both remote and local notifications. You get a lot of power in a package that keeps thing simple.
The Whole Code
A downloadable version of this project can be found here.
// // ViewController.swift // NotificationManagementDemo // // Created by Steven Lipton on 11/18/16. // Copyright © 2016 Steven Lipton. All rights reserved. // import UIKit import UserNotifications class ViewController: UIViewController,UNUserNotificationCenterDelegate { var isGrantedNotificationAccess = false var pressed = 0 @IBAction func setNotification(_ sender: UIButton) { if isGrantedNotificationAccess{ //set content let content = UNMutableNotificationContent() content.title = "My Notification Management Demo" content.subtitle = "Timed Notification" content.body = "Notification pressed" pressed += 1 content.body = "Notification pressed \(pressed) times" content.categoryIdentifier = "message" //set trigger /*let trigger = UNTimeIntervalNotificationTrigger( timeInterval: 10.0, repeats: false)*/ let trigger = UNTimeIntervalNotificationTrigger( timeInterval: 60.0, repeats: true) //Create the request let request = UNNotificationRequest( identifier: "my.notification", content: content, trigger: trigger ) //Schedule the request UNUserNotificationCenter.current().add( request, withCompletionHandler: nil) } } @IBAction func listNotification(_ sender: UIButton) { UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: {requests -> () in print("\(requests.count) requests -------") for request in requests{ print(request.identifier) } }) UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: {deliveredNotifications -> () in print("\(deliveredNotifications.count) Delivered notifications-------") for notification in deliveredNotifications{ print(notification.request.identifier) } }) } @IBAction func removeNotification(_ sender: UIButton) { UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["my.notification"]) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. UNUserNotificationCenter.current().requestAuthorization( options: [.alert,.sound,.badge], completionHandler: { (granted,error) in self.isGrantedNotificationAccess = granted if !granted{ //add alert to complain } }) UNUserNotificationCenter.current().delegate = self } //MARK: Delegates func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert,.sound]) /* Not a very good way to do this, just here to give you ideas. let alert = UIAlertController( title: notification.request.content.title, message: notification.request.content.body, preferredStyle: .alert) let okAction = UIAlertAction( title: "OK", style: .default, handler: nil) alert.addAction(okAction) present(alert, animated: true, completion: nil) */ } }
Leave a Reply to Peter Lewis Cancel reply