iOS 10 UNUserNotificationCenterDelegate Overview

iOS 10 UNUserNotificationCenterDelegate

I just finished watching “Introduction to Notifications” from WWDC 2016 and there are some really cool new features for iOS 10 local notifications. Notifications are core to the mobile experience, regardless of what platform you’re using – Android, iOS, or something else. Apple really raised the bar with their iOS 10 local notifications through creation of a new framework, the User Notifications framework. The brand new iOS 10 UNUserNotificationCenterDelegate in the User Notifications Framework is one thing that jumped out at me.

Why Create A New Framework?

Push notifications have been available in iOS for years. Just like any framework, over time, as use cases and technology evolves, frameworks need to be reconsidered and improved. It is that time for push notifications on iOS. New in iOS 10, Apple created the User Notifications Framework. When implementing notifications, you have the choice to implement a local or remote notification. A local notification does not require a server to alert the user, where a remote notification does require a remote server to send information to the user. In past iOS versions, depending on the type of notification you are handling, there are two different methods in UIApplicationDelegate to implement to handle either notification type – remote or local. As such, this can lead to a lot of duplicate code since it’s likely that there is common handling of notifications regardless of whether they are local or remote.

One Notification Handler To Rule Them All

The User Notification Framework in iOS 10 fixes this as there is now a single method for handling both types of notifications. The new iOS 10 UNUserNotificationCenterDelegate now has a single set of methods for handling both remote and local notifications.

Deprecated

The old methods on UIApplicationDelegate are deprecated, including:

  • application(_:didReceive:) – Sent to the delegate when a running app receives a local notification.
  • application(_:didReceiveRemoteNotification:) – Called when your app has received a remote notification
  • application(_:handleActionWithIdentifier:forRemoteNotification:completionHandler:) – Tells the app delegate to perform the custom action specified by a remote notification.
  • application(_:handleActionWithIdentifier:for:withResponseInfo:completionHandler:) – Called when your app has been activated by the user selecting an action from a local notification.
  • application(_:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:) – Called when your app has been activated by the user selecting an action from a remote notification.

New

Instead, these are all consolidated into a TWO METHODS in the new iOS 10 UNUserNotificationCenterDelegate protocol:

  • userNotificationCenter(_:didReceive:withCompletionHandler:) – Called to let your app know which action was selected by the user for a given notification.
  • userNotificationCenter(_:willPresent:withCompletionHandler:) – Delivers a notification to an app running in the foreground.

That’s right, two methods. And what’s better, is that now that they are moved into their own protocol, the iOS 10 UNUserNotificationCenterDelegate so this will help clean up your existing UIApplicationDelegate by being able to refactor all that old notification handling code into a shiny, new, cohesive protocol of it’s own.

Here’s an example:

extension NotificationManager: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {

        switch response.actionIdentifier {

        // NotificationActions is a custom String enum I've defined
        case NotificationActions.HighFive.rawValue:
            print("High Five Delivered!")
        default: break
        }
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {

        // Delivers a notification to an app running in the foreground.
    }
}

That’s literally it for handling notifications.

Bonus: Creating Local Notifications

Similar, creating local notifications has also been simplified. In order to verify the behavior of the new iOS 10 UNUserNotificationCenterDelegate protocol, I wrote some sample iOS 10 User Notifications Framework code to generate a local notification:

// 1
let highFiveAction = UNNotificationAction(identifier: NotificationActions.HighFive.rawValue, title: "High Five", options: [])
let category = UNNotificationCategory(identifier: "wassup", actions: [highFiveAction], minimalActions: [highFiveAction], intentIdentifiers: [], options: [.customDismissAction])
UNUserNotificationCenter.current().setNotificationCategories([category])

// 2
let highFiveContent = UNMutableNotificationContent()
highFiveContent.title = "Wassup?"
highFiveContent.body = "Can I get a high five?"

// 3
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

// 4
let highFiveRequestIdentifier = "sampleRequest"
let highFiveRequest = UNNotificationRequest(identifier: highFiveRequestIdentifier, content: highFiveContent, trigger: trigger)
UNUserNotificationCenter.current().add(highFiveRequest) { (error) in
  // handle the error if needed
  print(error)
}

Here’s an overview of that code block:

  1. Create the custom “High Five” action for responding to the local notification, and set it as a category for the notification in addition to a system provided category for simply “dismissing” the notification.
  2. Create the content for the local notification.
  3. Create the trigger for the notification, for it to pop 5 seconds in the future.
  4. Add the notification to the notification system.

This code highlights two new features of local notifications in iOS 10, custom actions and time based triggers.

Custom actions now let you specify any number of actions for which the end user may respond to the notification.

iOS 10 UNUserNotificationCenterDelegate

Time based triggers allow you to delay the local notification by a time interval, designate it as repeating or not, and even schedule it based on an actual date.

I didn’t hear it confirmed in the WWDC presentation, but I’m pretty sure that the custom notification actions require 3D Touch. I could not figure out how to interact with them on my iPhone 5s device that is running iOS 10.

I’ve even bundled all this code up into an Xcode project for you on GitHub, here.

Notify Me

Notifications, remote and local, enable some of my favorite features on iOS so as an end user, I’m happy to see all these enhancements. As a developer, I can’t wait to try them out in my own app. This only scratches the surface of the new stuff Apple added. Do you use notifications in your app? What are you looking forward to most about the new iOS 10 User Notifications Framework? What about the iOS 10 UNUserNotificationCenterDelegate?

Happy cleaning.

24 thoughts on “iOS 10 UNUserNotificationCenterDelegate Overview”

  1. Hi, I wonder what is the replacement for former AppDelegate’s didReceiveLocalNotification in case I am just interested in getting the arrival of the notification BEFORE the user actions on it?
    Does it mean, userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) will be called twice?
    I need to handle the pure incoming immediately and later the user-action

    1. Basti – Thanks for reading. Just to confirm, it sounds like you want to first be notified that the local notification was presented, and then, also get notified once the user took action on the notification? My understanding is this:

      userNotificationCenter(_:willPresent:withCompletionHandler:) – This is called when the notification is about to be presented.

      userNotificationCenter(_:didReceive:withCompletionHandler:) – This is called when the user takes action on the notification.

      So now, willPresent won’t be called twice.

      Did you check out my sample project at https://github.com/obuseme/NotificationExample? I have all the code wired up for a local notification if you want to hack at it to learn more.

      Also, check out the new UNUserNotificationCenter class. I didn’t mention it in the post, but it has some cool methods for accessing notifications that have already been posted so you can actually either update their content (like if a score on a sports game changes), or even just remove them (like if the notification no longer applies). Hope this helps.

  2. Hi,

    You can display custom actions and interact with them on non 3d touch devices. Just swipe down on the notification. Works like a charm!

  3. Hi,
    Is it possible to use the User Notifications Framework to customize the view of a notification received for example from Skype or Twitter? For example, I want to show a custom pop up window when a skype message os received in my phone.
    Thank you.

  4. Hi,
    I am trying to make our app’s push notification work on iOS 10 and have found application:didReceiveRemoteNotification:fetchCompletionHandler: won’t be called when app is in background with xcode 8. The new function from UNUserNotificationCenterDelegate: func userNotificationCenter(UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: (UNNotificationPresentationOptions) -> Void) looks like only for a foreground app. So how could we handle the app background case?
    Besides this, does anyone know how to make the new two functions for receiving notifications and handling the selections of custom actions to be called in xcode? My breakpoint didn’t work with them.

    Thanks!

      1. Hi Brian,
        Thanks for sharing this Radar. Could you let me know when our issue is resolved?

        Thanks.
        Yisong

    1. Actually it does, it is likely your payload is now incorrect for delivery to iOS10. Examine what you are sending, and ensure you have at least one of the three main attributes (badge, sound, alert .. if memory serves). If not, Apple eats the notification for a snack.

      This article is good but incomplete as it ignores the entire receiving of remote notif’s while the app is not running. This is a main use-case that must be addressed.

    2. func userNotificationCenter(_ center: UNUserNotificationCenter,
      didReceive response: UNNotificationResponse,
      withCompletionHandler completionHandler: @escaping () -> Void)
      This method does receive notification when app is in the background.

  5. I implemented your example, and I am able to schedule notifications and receive them. But I do not see any actions, and didReceiveLocalNotification is still called when notification is received.

  6. “[…] two new features of local notifications in iOS 10, custom actions and time based triggers.”

    Sure, we can’t use custom actions in iOS9 for local notifications, and triggering local notifications was not based on time… –‘

    1. Damien – I’m not sure what you’re getting at. Are you just confirming? Thanks for reading.

  7. Hi there,

    I have my notifications functioning properly but now I’m trying to add some extra functionality. When an action is selected I would like to present the user with a particular view. I have all the code to handle the action being selected but i’m unsure how I should instantiate that view. I have setup my notifications in a similar fashion as the sample project about where I have a class that pertains just to notifications.

  8. Hi there,

    I have my notifications functioning properly but now I’m trying to add some extra functionality. When an action is selected I would like to present the user with a particular view. I have all the code to handle the action being selected but i’m unsure how I should instantiate that view. I have setup my notifications in a similar fashion as the sample project about where I have a class that pertains just to notifications.

    Thanks

  9. I am getting an error in the downloaded sample as follows:

    let category = UNNotificationCategory(identifier: “wassup”, actions: [highFiveAction], minimalActions: [highFiveAction], intentIdentifiers: [], options: [.customDismissAction])
    UNUserNotificationCenter.current().setNotificationCategories([category])

    the error is highlighting the ” .customDismissAction ” and saying ” Type of expression is ambiguous without more context ”

    I am using Xcode 8 swift 3

    Any idea what is happening?

  10. Hi, Andy. Great post!

    The code sample you have posted will not compile and run in XCode 8.2.1 with iOS 10.2

    Here’s what I get:

    class NotificationManager: NSObject {

    func registerForNotifications() {
    UNUserNotificationCenter.current().requestAuthorization([.alert, .sound, .badge]) { (granted, error) in
    if granted {
    self.setupAndGenerateLocalNotification()
    }
    }
    }
    Red error: “Missing argument label ‘options:’ in call

    and also

    let category = UNNotificationCategory(identifier: “wassup”, actions: [highFiveAction], minimalActions: [highFiveAction], intentIdentifiers: [], options: [.customDismissAction])

    Red error: “Type of expression is ambiguous without more context.”

    And yellow errors that are mystifying, with no workable suggestions from XCode:

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {

    Yellow error: “Instance method ‘userNotificationCenter(…didReceiveWithCompletionHandler:)” nearly matches optional requirement ‘userNotificationCenter(_didReceiveWithCompletionHandler:)’ of protocol ‘UNUserNotificationCenterDelegate’

    but the XCode suggested fixes don’t work.

    Lastly,

    “func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent” has the same yellow warning.

    Do you know a fix for this? I’m trying without success to find one working piece of sample code that delivers a notification sound for an app in the foreground.

  11. Hi,

    willPresent notification only works when the app in in the foreground. I’d like to perform some actions *before* the notification is presented to the user, and when the application is in the foreground. Can I do that with local notifications or do I have to use push notifications? If so, is Firebase the way to go?
    Thank you.

  12. Sorry, I meant to say that I need to perform the actions when the application is in the *background*, and before the alert is presented to the user.

Leave a Reply

Your email address will not be published. Required fields are marked *