With iOS 10, tvOS 10 and watchOS 3 launch Apple introduced the new UserNotifications framework. It supports the delivery and handling of local and remote notifications. Cool, but you can say that it was already possible through UIKit and classes like UILocalNotification or dictionaries for remote notifications. And you’re completely right, but these new possibilities, in particular, are extremely convenient and powerful. Take a look at it with me!

Let’s start with a little disclaimer: the new framework works great if you target your app for iOS 10 or newer. Otherwise, you have to use deprecated objects or implement it twice for iOS older and newer than 10.0. You can do this like that:

if #available(iOS 10.0, *) {
    // UserNotifications framework here
} else {
    // Old implementation here
}

Okay, now when we know everything about the limitations, we can start with these shiny new features.

Notification state

Before you schedule the very first local notification, you should register your app for receiving user notifications at all.

First, remember to import UserNotifications. Then request authorization through the UNUserNotificationCenter singleton object.

import UserNotifications

func requestAuthorization() {
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
        print("Access granted: \(granted.description)")
    }
}

I guess that you’ve already noticed that the new framework gives you information about the authorization status just after authorization. That’s something that was missing in the elder framework. You can also get even more precise status thanks to getNotificationSettings:.

UNUserNotificationCenter.current().getNotificationSettings { notificationSettings in
    let status = notificationSettings.authorizationStatus
    let sound = notificationSettings.soundSetting
    let badge = notificationSettings.badgeSetting
    let alert = notificationSettings.alertSetting
}

The notificationSettings is an UNNotificationSettings object, which contains multiple enums giving the current status of an app notification settings. It’s pretty straightforward and let you do things that were not allowed in iOS 9.

Triggers

iOS 9 and elder don’t let you easily set repeating local notification. Fortunately, there were little libraries like our in-company Habit making this a bit easier.

Source: Introduction to Notifications – WWDC 2016
iOS Notification Triggers. Source: Introduction to Notifications – WWDC 2016

iOS 10 notifications use simple and powerful triggering mechanism. Let’s say you need an event that fires every hour. You can achieve that doing this:

// 1) Create a notification content
let notificationContent = UNMutableNotificationContent()
notificationContent.title = "Droids On Roids"

// 2) Create a DateComponents for every o'clock hour
var dateComponents = DateComponents()
dateComponents.minute = 0
dateComponents.second = 0

// 3) Create a trigger based on the matching date
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)

// 4) Create a request with the given identifier content and trigger
let request = UNNotificationRequest(identifier: "myNotification", content: notificationContent, trigger: trigger)

// 5) Add the request to the UserNotificationCenter
UNUserNotificationCenter.current().add(request) { error in
    guard let error = error else { return }
    print(error.localizedDescription)
}

Boring? What about a trigger based on location? This technique is used by Reminders.app and in iOS 10 it can look like this:

let droidsOnRoidsCoordinate = CLLocationCoordinate2D(latitude: 51.108989, longitude: 17.029211)
let region = CLCircularRegion(center: droidsOnRoidsCoordinate, radius: 100.0, identifier: "Droids On Roids, pl. Solny 15, Wrocław")
let locationTrigger = UNLocationNotificationTrigger(region: region, repeats: false)

Then just use this trigger the same as in the previous example and you’ll receive a notification when to find yourself in 100 meters radius from our office!

Push notifications

You can use UNUserNotificationCenter‘s delegate to receive and handle push (and local – if you want) notifications.

import UserNotifications

@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        
        return true
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        if response.notification.request.trigger is UNPushNotificationTrigger {
            // Do something with received push notification (response.notification)
        }

        completionHandler()
    }
}

Actions

iOS 10 notifications offer custom action. In the example, you can get a notification from your clinic about an upcoming visit and agree (confirm) or decline (resign) the term even without opening the app.

iOS 10 Notification Action

I’ve got a much simpler example with the action that doesn’t like to be tapped and if you’re stubborn, printing you boring lorem ipsum.

let action = UNNotificationAction(identifier: "pleaseDoNotTap", title: "DON'T TAP ME", options: .destructive)
let category = UNNotificationCategory(identifier: "category", actions: [action], intentIdentifiers: [], options: .customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories([category])

It’s all about setting notification categories with actions. In the example, I use only one category with one action inside. Then just set categoryIdentifier of your UNNotificationContent  to the proper identifier.

UNUserNotificationCenter.current().delegate = self
notificationContent.categoryIdentifier = "category"

I also set up a delegate to the current view controller, because it works thank to userNotificationCenter(_:didReceive:withCompletionHandler:) method too.

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    if response.actionIdentifier == "pleaseDoNotTap" {
        print("=== I SAID NO! ===")
        print("Read >> Lorem ipsum << as a punish!")
        print(loremIpsum)
    }
    
    completionHandler()
}

UserNotificationsUI

I guess that’s not all about UserNotifications, but there is also UserNotificationsUI, which lets you customise the appearance of local and remote notifications. iOS 10 gives you the great opportunity to update your apps and many of us already did it (according to appreviewtimes.com average review time has increased from 1 to 3 days and I don’t think that’s because Apple fired some reviewers ;)). Why do not implement new notifications right now?