An Introduction to the User Notifications Framework

Local Notifications With the User Notifications Framework

An Introduction to the User Notifications Framework
1 Local Notifications With the User Notifications Framework
2 Actionable Notifications With the User Notifications Framework

As of iOS 10, UILocalNotification is deprecated. Scheduling and handling local notifications is now the responsibility of the User Notifications framework, a brand new framework that is much more capable than the UILocalNotification class.

The UILocalNotification class is deprecated as of iOS 10.

In this tutorial, I show you how to use the User Notifications framework to schedule and handle local notifications.

Project Setup

I don't want to overload you with theory. Instead, we are going to build a small application that schedules and displays local notifications. Open Xcode and create a new project based on the Single View Application template.

Project Setup

Name the project LocalNotifications, set Language to Swift, and Devices to iPhone.

Project Setup

Creating the User Interface

The idea is simple. When the user taps a button, a local notification is scheduled with a time interval. Open ViewController.swift and create an action. You can leave the implementation empty for now.

import UIKit

class ViewController: UIViewController {

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: - Actions

    @IBAction func didTapButton(sender: UIButton) {

    }

}

Open Main.storyboard and add a button to the View Controller Scene. Set the title of the button and connect the Touch Up Inside event to the didTapButton(sender:) action.

Creating the User Interface

Revisit ViewController.swift and add an import statement for the User Notifications framework. We are ready to start working with the User Notifications framework.

import UIKit
import UserNotifications

class ViewController: UIViewController {

    ...

}

Inspecting the Application's Notification Settings

In the early days of iOS, an application could schedule local notifications without the user's consent. In iOS 8, Apple introduced registerUserNotificationSettings(_:) to request the user's permission to display local and remote notifications or update the application's badge number. Even though the User Notifications framework changes the flow once more, the result is a concise interface with a few additional features that you are certainly going to appreciate.

When the user taps the button of the view controller, we need to ask the User Notifications framework whether the application has the permission to display notifications, local and remote. The framework doesn't make a distinction between local and remote notifications. That is a good thing as it greatly simplifies notification handling.

The class we need to interact with to obtain this information is the UNUserNotificationCenter class. The current() class method gives us access to the shared UNUserNotificationCenter instance. We ask it for the notification settings by invoking getNotificationSettings(completionHandler:). The method accepts a completion handler with one parameter, an instance of the UNNotificationSettings class. This is what the didTapButton(sender: UIButton) action should look like.

@IBAction func didTapButton(sender: UIButton) {
    // Request Notification Settings
    UNUserNotificationCenter.current().getNotificationSettings { (notificationSettings) in
        switch notificationSettings.authorizationStatus {
        case .notDetermined:
            // Request Authorization
        case .authorized:
            // Schedule Local Notification
        case .denied:
            print("Application Not Allowed to Display Notifications")
        }
    }
}

Why do we ask for the notification settings of the application? We do this to determine if the application previously requested the user's permission. With the UNNotificationSettings class, Apple has given developers more insight into the user's notification settings of your application.

The property we are interested in is the authorizationStatus property. It tells us if the application previously prompted the user for permission to display notifications. If the value of authorizationStatus is equal to notDetermined, we first need to request the user's permission. If the application was already granted permission to display notifications, we can go ahead and schedule a local notification.

Requesting Authorization

To request authorization for displaying notifications, we make use of a helper method, requestAuthorization(completionHandler:). The method accepts a completion handler with one parameter, a boolean that indicates whether the application was granted permission to display notifications.

@IBAction func didTapButton(sender: UIButton) {
    // Request Notification Settings
    UNUserNotificationCenter.current().getNotificationSettings { (notificationSettings) in
        switch notificationSettings.authorizationStatus {
        case .notDetermined:
            self.requestAuthorization(completionHandler: { (success) in
                guard success else { return }

                // Schedule Local Notification
            })
        case .authorized:
            // Schedule Local Notification
        case .denied:
            print("Application Not Allowed to Display Notifications")
        }
    }
}

The implementation of requestAuthorization(completionHandler:) is not difficult. We ask the user's permission by invoking requestAuthorization(options:completionHandler:) on the shared user notification center. The method defines two parameters:

  • a set of options that defines what type of notifications the application would like to display
  • a completion handler that tells the application the result of the request
// MARK: - Private Methods

private func requestAuthorization(completionHandler: @escaping (_ success: Bool) -> ()) {
    // Request Authorization
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (success, error) in
        if let error = error {
            print("Request Authorization Failed (\(error), \(error.localizedDescription))")
        }

        completionHandler(success)
    }
}

The set of options can include the following values:

  • badge: update the application's badge number
  • sound: play sound when a notification arrives
  • alert: display notifications
  • carPlay: display notifications in a CarPlay environment

The UNNotificationSettings class lets your inspect the permissions for your application. For example, you can ask the User Notifications framework if your application can update the badge number by asking the UNNotificationSettings instance for the value of its badgeSetting property.

Scheduling a Notification

We can now schedule a local notification. Update the didTapButton(sender:) action as shown below. We implement the scheduleLocalNotification() method in a moment.

@IBAction func didTapButton(sender: UIButton) {
    // Request Notification Settings
    UNUserNotificationCenter.current().getNotificationSettings { (notificationSettings) in
        switch notificationSettings.authorizationStatus {
        case .notDetermined:
            self.requestAuthorization(completionHandler: { (success) in
                guard success else { return }

                // Schedule Local Notification
                self.scheduleLocalNotification()
            })
        case .authorized:
            // Schedule Local Notification
            self.scheduleLocalNotification()
        case .denied:
            print("Application Not Allowed to Display Notifications")
        }
    }
}

The API for scheduling a local notification may seem a bit daunting at first glance, but it offers a lot of power and flexibility. This is what the implementation of the scheduleLocalNotification() method looks like.

private func scheduleLocalNotification() {
    // Create Notification Content
    let notificationContent = UNMutableNotificationContent()

    // Configure Notification Content
    notificationContent.title = "Cocoacasts"
    notificationContent.subtitle = "Local Notifications"
    notificationContent.body = "In this tutorial, you learn how to schedule local notifications with the User Notifications framework."

    // Add Trigger
    let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 10.0, repeats: false)

    // Create Notification Request
    let notificationRequest = UNNotificationRequest(identifier: "cocoacasts_local_notification", content: notificationContent, trigger: notificationTrigger)

    // Add Request to User Notification Center
    UNUserNotificationCenter.current().add(notificationRequest) { (error) in
        if let error = error {
            print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
        }
    }
}

We first define the content of the local notification by creating an instance the UNMutableNotificationContent class and populating it with data. As of iOS 10, you can define a title as well as a subtitle.

// Create Notification Content
let notificationContent = UNMutableNotificationContent()

// Configure Notification Content
notificationContent.title = "Cocoacasts"
notificationContent.subtitle = "Local Notifications"
notificationContent.body = "In this tutorial, you learn how to schedule local notifications with the User Notifications framework."

Next, we need to create a trigger for the local notification. As the name implies, we need to tell the User Notifications framework when the local notification should be shown.

// Add Trigger
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 10.0, repeats: false)

In this example, we use an instance of the UNTimeIntervalNotificationTrigger class. We tell the User Notifications framework that the local notification should be displayed ten seconds from now and we don't want to repeat the local notification.

There are several other triggers. You can show the local notification on a particular date and time (UNCalendarNotificationTrigger) or you can show the local notification when the user's device enters a geofence (UNLocationNotificationTrigger) by defining a region, an instance of the CLRegion class.

To schedule the local notification, we create a notification request, an instance of the UNNotificationRequest class. Each notification request has an identifier, content, and a trigger.

// Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "cocoacasts_local_notification", content: notificationContent, trigger: notificationTrigger)

// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
    if let error = error {
        print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
    }
}

The request is added to the shared user notification center by invoking add(_:withCompletionHandler:). This method accepts the notification request and a completion handler. The completion handler informs the application about the result of the operation.

Implementing the Delegate Protocol

If you run the application, you won't see a local notification pop up as long as the application is in the foreground. Why is that? The UNUserNotificationCenter class defines the UNUserNotificationCenterDelegate protocol. This protocol gives your application even more control over how the local notification is handled.

Start by making the ViewController class the delegate of the shared user notification center. Update the viewDidLoad() method as shown below.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Configure User Notification Center
    UNUserNotificationCenter.current().delegate = self
}

Next, create an extension for the ViewController class in ViewController.swift. We only need to implement one method.

extension ViewController: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert])
    }

}

The userNotificationCenter(_:willPresent:withCompletionHandler:) method is invoked when a local notification arrives and the application is in the foreground. To display the local notification when the application is in the foreground, we need to invoke the completion handler and pass in a set of presentation options (UNNotificationPresentationOptions). The options determine how the local notification is displayed. In the example, we tell the User Notifications framework to display the local notification (.alert).

If you run the application and tap the button, you should see a local notification appear after ten seconds.

Displaying a Local Notification

If you pull down the local notification, you can see more details of the local notification.

Displaying a Local Notification

Note that userNotificationCenter(_:willPresent:withCompletionHandler:) is only invoked if your application is in the foreground. If your application is in the background, the local notification behaves as defined by the notification's content and the user's notification settings.

Handling the Notification

In the userNotificationCenter(_:willPresent:withCompletionHandler:) and userNotificationCenter(_:didReceive:withCompletionHandler:) methods of the UNUserNotificationCenterDelegate protocol, you decide how the application responds to the notification. Both methods hand you the notification object, an instance of the UNNotification class. This class gives you access to the notification request (UNNotificationRequest) that was used to add the notification.

let notificationRequest = notification.request

You can use the identifier of the notification request to determine what action the application should take in response to the notification. This also applies to remote notifications and it adds a lot of flexibility to local and remote notifications.

What's Next

In the next tutorial, I show you how to add actions to a notification. You can download the source files of this tutorial from GitHub.

Next Episode "Actionable Notifications With the User Notifications Framework"

Stop Writing Swift That Sucks

Download the Swift Patterns I Swear By