Make App Pie

Training for Developers and Artists

Manage, Delete and Update Notifications in iOS 10

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:

2016-11-18_08-49-23

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 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.

2016-11-17_06-37-21

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.

2016-11-18_09-09-46

Select Allow. Tap the Set Notification button. Wait ten seconds. The notification appears in the app.

2016-11-18_09-12-11

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.

2016-11-18_09-14-57

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:

2016-11-18_09-41-06

 

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)
         */
    }
}

18 responses to “Manage, Delete and Update Notifications in iOS 10”

  1. […] I discussed in more detail in another post, The UserNotifications framework can monitor pending notifications, the notifications waiting to […]

  2. How can I set another destination when I press the notification? That is, you see another screen.

    1. That might be difficult given that the notification is in Notification Center and not the app. Probably in the delegate with the action handlers, I’d guess.

    2. That is a little complicated but you can achieve it by setting an action identifier on the notification. Then in your UNNotificationCenter Delegate object you’ll handle the notification action. In your case you’ll want to instantiate the view controller you want to show and push it via the main navigation controller of your app. In this way the user will be able to also navigate back to the view she was in the app prior tapping on the action in the notification.
      Actually it’s easier to get done than to explain.

  3. Hi, thanks for the post! I have a question, this example is for local notifications. I’m not sure how to identify a push notification to be able to update it like you did here. I send push notification with the same identifier on the payload but it’s not replaced like you did. Thanks

    1. Most of this won’t apply to Push notifications, because that’s a function of the server, not the app client of the push. Two things about modifications though: If the push notification has the same request identifier each time, it will update itself. Secondly, modifications to a push notification can be done in the service extension. See my article https://makeapppie.com/2017/01/24/using-user-notifications-service-extensions-in/ for more about the service extension or check my course on push notifications at Lynda.com

      1. Thanks!!

  4. You have shown how easy it is to delete pending/delivered local notifications when the app is in the foreground. Can I remove a delivered local notification when the app is NOT in the foreground? (if I already know exactly when I want it to be removed)

  5. Thanks. Nice explanation. i have question i need to display local notification every day same time in various content. Content not updated in same request. Any way to handle this scenario?

  6. Thanks for this, good explanation.
    But my requirement is I don’t want to display the same content type of multiple notifications to Notification Tray. I am working with Firebase Push Notifications.

    1. I’m afraid I can’t help you there.

  7. Thank you for sharing the valuable information about the react native push notifications

  8. Is there a limit to the number of pending notifications? I have an app that sets up a notification for each item in an array. There are 50 items, but only the last 35 notifications are pending. I know that all 50 requests were sent. Is that a defined limit?

    1. Yes, there is. Both time and quantity limits. I don’t remember what they are off the top of my head.

      1. OK, thanks. I don’t really have to know what the limits are, just that they exist. I can work around that.

  9. The limit.is 64. The Apple documentation says that the soonest scheduled notifications survive, but my observations show that it’s the most recently activated notifications that survive.

    Can anyone else confirm this?

Leave a Reply to Peter Lewis Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: