Notifications provide a mechanism for an app to schedule an alert to notify the user about an event. These notifications take the form of a notification panel containing a message accompanied by a sound and the device’s vibration.
Notifications are categorized as either local or remote. Notifications initiated by apps running on a device are referred to as local notifications. On the other hand, a remote notification is initiated by a remote server and pushed to the device, where it is displayed to the user.
Both local and remote notifications are managed using the classes of the UserNotifications framework in conjunction with the user notification center. While covering the steps to create, send and manage local notifications, this chapter will also outline the various classes provided within the UserNotifications framework and the methods provided by the user notification center.
Creating the Local Notification App Project
The first step in demonstrating the use of local notifications is to create a new Xcode project. Begin by launching Xcode and creating a new project using the iOS App template with the Swift and Storyboard options selected, entering NotifyDemo as the product name.
Requesting Notification Authorization
Before an app can issue notifications, it must first seek permission from the user. This request involves making a call to the user notification center.
The user notification center handles, manages, and coordinates all notifications on a device. In this case, a reference to the current notification center instance needs to be obtained, and the requestAuthorization method is called on that object.
Edit the ViewController.swift file to import the UserNotifications framework, request authorization, and add a variable to store the subtitle of the message, which the user will be able to change from within the notification later in this tutorial:
import UIKit
import UserNotifications
class ViewController: UIViewController {
var messageSubtitle = "Staff Meeting in 20 minutes"
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().requestAuthorization(options:
[[.alert, .sound, .badge]],
completionHandler: { (granted, error) in
// Handle Error
})
}
.
.
}
Code language: Swift (swift)
Run the app on an iOS device and tap the Allow button when the permission request dialog (Figure 87-1) appears:
Designing the User Interface
Select the Main.storyboard file and drag and drop a Button object to position it in the center of the scene. Then, using the Auto Layout Resolve Auto Layout Issues menu, select the option to reset to suggested constraints for all views in the view controller.
Change the text on the button to read Send Notification, display the Assistant Editor panel and establish an action connection from the button to a method named buttonPressed.
Edit the ViewController.swift file and modify the buttonPressed action method to call a method named sendNotification:
@IBAction func buttonPressed(_ sender: Any) {
sendNotification()
}
Code language: Swift (swift)
On completion of the layout work, the user interface should match that shown in Figure 87-2 below:
Creating the Message Content
The first part of the notification to be created is the message content. An instance of the UNMutableNotificationContent class represents the content of the message for a local notification. This class can contain various options, including the message title, subtitle, and body text. In addition, the message may contain media content such as images and videos. Within the ViewController.swift file, add the sendNotification method as follows:
func sendNotification() {
let content = UNMutableNotificationContent()
content.title = "Meeting Reminder"
content.subtitle = messageSubtitle
content.body = "Don't forget to bring coffee."
content.badge = 1
}
Code language: Swift (swift)
Note also that the badge property of the content object is set to 1. This value configures the notification count that is to appear on the app launch icon on the device after a notification has been triggered, as illustrated in Figure 87-3:
Specifying a Notification Trigger
Once the content of the message has been created, a trigger needs to be defined that will cause the notification to be presented to the user. Local notifications can be triggered based on elapsed time intervals, a specific date and time, or a location change. For this example, the notification will be configured to trigger after 5 seconds have elapsed (without repeating):
func sendNotification() {
let content = UNMutableNotificationContent()
content.title = "Meeting Reminder"
content.subtitle = messageSubtitle
content.body = "Don't forget to bring coffee."
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5,
repeats: false)
}
Code language: Swift (swift)
Creating the Notification Request
The next step is to create a notification request object containing the content and trigger objects together with an identifier that can be used to access the notification later if it needs to be modified or deleted. The notification request takes the form of a UNNotificationRequest object, the code for which will now need to be added to the sendNotification method:
func sendNotification() {
let content = UNMutableNotificationContent()
content.title = "Meeting Reminder"
content.subtitle = messageSubtitle
content.body = "Don't forget to bring coffee."
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5,
repeats: false)
let requestIdentifier = "demoNotification"
let request = UNNotificationRequest(identifier: requestIdentifier,
content: content, trigger: trigger)
}
Code language: Swift (swift)
Adding the Request
The request object now needs to be added to the notification center, where it will be triggered when the specified time has elapsed:
func sendNotification() {
let content = UNMutableNotificationContent()
content.title = "Meeting Reminder"
content.subtitle = messageSubtitle
content.body = "Don't forget to bring coffee."
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5,
repeats: false)
let requestIdentifier = "demoNotification"
let request = UNNotificationRequest(identifier: requestIdentifier,
content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request,
withCompletionHandler: { (error) in
// Handle error
})
}
Code language: Swift (swift)
Testing the Notification
Compile and run the NotifyDemo app on a device or simulator session. Once the app has loaded, tap the Send Notification button and press the Home button to place the app in the background. After 5 seconds have elapsed, the notification will appear as shown in Figure 87-4:
Open the app again and tap the button, but do not place the app in the background. This time the notification will not appear. This is the default behavior for user notifications. If the notifications issued by an app are to appear while that app is in the foreground, an additional step is necessary.
Receiving Notifications in the Foreground
As demonstrated in the previous section, when an app that issues a notification is currently in the foreground, the notification is not displayed. To change this default behavior, it will be necessary for the view controller to declare itself as conforming to the UNUserNotificationCenterDelegate protocol and implement the userNotification:willPresent method. In addition, the current class will also need to be declared as the notification delegate. Remaining within the ViewController.swift file, make these changes as follows:
import UIKit
import UserNotifications
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
.
.
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().requestAuthorization(options: [[.alert,
.sound, .badge]], completionHandler: { (granted, error) in
// Handle Error
})
UNUserNotificationCenter.current().delegate = self
}
.
.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .sound])
}
Code language: Swift (swift)
The userNotification:willPresent method calls the provided completion handler, indicating that the notification should be presented to the user using both the banner message and sound. Rerun the app and test that the notification banner appears after the button is pressed with app remaining in the foreground.
Adding Notification Actions
The default action when the user taps a notification is to dismiss the notification and launch the corresponding app. However, the UserNotifications framework also allows action buttons to be added to notifications. These buttons are displayed when the user presses down with force on the notification message or swipes the message to the left. For this example, two action buttons will be added to the notification, one instructing the app to repeat the notification while the other will allow the user to input different text to appear in the message subtitle before repeating the notification.
This will require the creation of two notification action objects as follows:
let repeatAction = UNNotificationAction(identifier: "repeat",
title: "Repeat", options:[])
let changeAction = UNTextInputNotificationAction(identifier: "change",
title: "Change Message", options: [])
Code language: Swift (swift)
Next, these action objects need to be placed in a notification category object, and the category object added to the user notification center after being assigned an identifier:
let category = UNNotificationCategory(identifier: "actionCategory",
actions: [repeatAction, changeAction],
intentIdentifiers: [], options: [])
content.categoryIdentifier = "actionCategory"
UNUserNotificationCenter.current().setNotificationCategories([category])
Code language: Swift (swift)
Combining these steps with the existing sendNotification method results in code that reads as follows:
func sendNotification() {
let content = UNMutableNotificationContent()
content.title = "Meeting Reminder"
content.subtitle = messageSubtitle
content.body = "Don't forget to bring coffee."
content.badge = 1
let repeatAction = UNNotificationAction(identifier:"repeat",
title: "Repeat", options: [])
let changeAction = UNTextInputNotificationAction(identifier:
"change", title: "Change Message", options: [])
let category = UNNotificationCategory(identifier: "actionCategory",
actions: [repeatAction, changeAction],
intentIdentifiers: [], options: [])
content.categoryIdentifier = "actionCategory"
UNUserNotificationCenter.current().setNotificationCategories(
[category])
.
.
}
Code language: Swift (swift)
Compile and run the app on a physical device, trigger the notification, and perform a long press on the message when it appears. The action buttons should appear beneath the message as illustrated in Figure 87-5 below:
Tap the Change Message button and note that a text input field appears. Although the action buttons are now present, some work still needs to be performed to handle the actions within the view controller.
Handling Notification Actions
When an action is selected by the user, the userNotification:didReceive method of the designated notification delegate is called by the user notification center. Since the ViewController class has already been declared as implementing the UNUserNotificationCenterDelegate protocol and assigned as the delegate, all that needs to be added to the ViewController.swift file is the userNotification:didReceive method:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive
response: UNNotificationResponse, withCompletionHandler completionHandler:
@escaping () -> Void) {
switch response.actionIdentifier {
case "repeat":
self.sendNotification()
case "change":
let textResponse = response
as! UNTextInputNotificationResponse
messageSubtitle = textResponse.userText
self.sendNotification()
default:
break
}
completionHandler()
}
Code language: Swift (swift)
The method is passed a UNNotificationResponse object from which we can extract the identifier of the action that triggered the call. A switch statement is then used to identify the action and take appropriate steps. If the repeat action is detected, the notification is resent. In the case of the change action, the text entered by the user is obtained from the response object and assigned to the messageSubtitle variable before the notification is sent again.
Compile and run the app again and verify that the actions perform as expected.
Hidden Notification Content
iOS provides a range of options that allow the user to control the information displayed within a notification when it appears on the device lock screen. As we have seen in this example, the default setting is for all of the information contained within a notification (in this case, the title, subtitle, and body content) to be displayed when the notification appears on the lock screen.
These notification preview settings can be specified per app or globally for all installed apps. To view and change the current global settings, open the Settings app and select Notifications -> Show Previews. As shown in Figure 87-6, options are available to show notification previews always, only when the device is unlocked or never:
These global settings can be overridden for individual apps from within the Settings app by displaying the Notifications screen, selecting the app from the list, and scrolling down to the Show Previews option, as highlighted in Figure 87-7:
The current notification preview settings configured by the user may be identified by accessing the showPreviewSettings property of the current notification center settings. For example:
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
switch settings.showPreviewsSetting {
case .always :
print("Always")
case .whenAuthenticated :
print("When unlocked")
case .never :
print("Never")
}
}
Code language: Swift (swift)
On the device on which the NotifyDemo app is installed, locate the notification preview settings for the app and change the current setting to Never. After making the change, run the app, trigger a notification, and immediately lock the device. When the notification appears, it will no longer contain any information except for the app name and the word Notification (Figure 87-8):
When creating a notification from within an app, it is possible to configure options to allow more information to be displayed. For example, when the notification code was implemented earlier in this chapter, a UNNotificationCategory object was created and initialized with information about the notification, including the identifier and the two actions. This category object was then assigned to the notification before it was sent:
.
.
let category = UNNotificationCategory(identifier: "actionCategory",
actions: [repeatAction, changeAction],
intentIdentifiers: [],
options: [])
content.categoryIdentifier = "actionCategory"
UNUserNotificationCenter.current().setNotificationCategories([category])
.
.
Code language: Swift (swift)
The category object also contains an array of options that were left empty in the above code. In this options array, properties can be declared that will allow the title, subtitle, or both to be displayed within the lock screen notification even when the preview mode is set to When Unlocked. The .hiddenPreviewsShowTitle and .hiddenPreviewsShowSubtitle option values represent the two settings. For example, modify the code for the NotifyDemo app so the category object is initialized as follows:
let category = UNNotificationCategory(identifier: "actionCategory",
actions: [repeatAction, changeAction],
intentIdentifiers: [],
options: [.hiddenPreviewsShowTitle])
Code language: Swift (swift)
Run the app, trigger a notification and lock the device. This time when the notification appears, the title is included in the preview though the subtitle and body remain hidden:
Modify the options array once more, this time adding the subtitle option as follows:
let category = UNNotificationCategory(identifier: "actionCategory",
actions: [repeatAction, changeAction],
intentIdentifiers: [],
options: [.hiddenPreviewsShowTitle,
.hiddenPreviewsShowSubtitle])
Code language: Swift (swift)
This time when a notification preview from the app appears on the lock screen, it will display both the title and subtitle while the body content remains hidden:
Managing Notifications
In addition to creating notifications, the UserNotifications framework also provides ways to manage notifications, both while they are still pending and after they have been delivered. One or more pending notifications may be removed by passing an array of request identifiers through to the removal method of the user notification center:
UNUserNotificationCenter.current()
.removePendingNotificationRequests(withIdentifiers:
[demoIdentifer, demoIdentifier2])
Code language: Swift (swift)
Similarly, the content of a pending intent may be updated by creating an updated request object with the same identifier containing the new content and adding it to the notifications center:
UNUserNotificationCenter.current().add(updatedRequest,
withCompletionHandler: { (error) in
// Handle error
})
Code language: Swift (swift)
Finally, previously delivered notifications can be deleted from the user’s notification history by a call to the user notification center passing through one or more request identifiers matching the notifications to be deleted:
UNUserNotificationCenter.current()
.removeDeliveredNotifications(withIdentifiers: [demoIdentifier])
Code language: Swift (swift)
Summary
An app can trigger notifications locally or remotely via a server. The UserNotifications framework manages both forms of notification. An app that needs to issue local notifications must first seek permission from the user.
A notification consists of a content object, a request object, and access to the notifications center. In addition, a notification may include actions that appear in the form of buttons when a force touch press is performed on the notification message. These actions include the ability for the user to input text to be sent to the app that initiated the intent.
iOS allows users to control content shown in the preview when the device is locked. The app can, however, configure options within the notification to display the title and subtitle content within the notification preview regardless of user settings.