Local Notifications with Swift 4
By: Ali Fakih
Tags: Swift Programming, iOS, Swift, Notification Center, Local Notificationswift

Before we begin, you can download the initial draft. The application is a table with a list of types of notifications.

Now, if you tap on any line, Alert pops up with the name of the notification. By the end of this tutorial, this action will send a local notification with the contents of the string. In addition, the user will be able to interact with this notification, choosing to do this or that action.

Before your application can send any notifications, the user must give their consent to this. To do this, when you first start the application, you must fulfill the request for sending notifications, so that the user can approve or reject it.To create such a request, go to the AppDelegate class and import the UserNotifications framework.

import UserNotifications

In the class properties, create a new notificationCenter property and assign an instance of the UNUserNotificationCenter class to it to manage notifications. This will be our notification center.

let notificationCenter = UNUserNotificationCenter.current()

Go to the didFinishLaunchingWithOptions method and declare the options , which will contain the options available for our notifications. In total, we now have four properties available: badge , sound , alert and carPlay .

let options: UNAuthorizationOptions = [.alert, .sound, .badge]

Having decided on the list of notification parameters, we can ask the user for permission to send notifications from our application.

notificationCenter.requestAuthorization(options: options) {

(didAllow, error) in

if !didAllow {

print("User has declined notifications")

}

}

If we start the application now, we will receive a request to send notifications.

The user can change the notification center settings of your application at any time. You can track these settings with the appropriate getNotificationSettings . This call contains the completion block and calls the UNNotificationSettings , which can be used to check the status of the authorization or certain notification parameters:

notificationCenter.getNotificationSettings { (settings) in

if settings.authorizationStatus != .authorized {

// Notifications not allowed

}

}

Create a notification request:

To send notifications, you need to create a notification request. Any notification contains specific content that can be configured through an instance of the UNMutableNotificationContent , which contains the following properties:

title : String data type for notification header.

: String data type for notification header. subtitle : String data type for subheading notifications. (Used as needed).

: String data type for subheading notifications. (Used as needed). body : A string containing the main text of the notification.

: A string containing the main text of the notification. badge : Number to display on the application icon.

: Number to display on the application icon. sound : Sound notification of delivery notification. To use the default sound, use UNNotificationSound.default() .

: Sound notification of delivery notification. To use the default sound, use . launchImageName : The name of the image file attached to the notification.

: The name of the image file attached to the notification. userInfo : A dictionary used to store user information that can be sent to a notification.

: A dictionary used to store user information that can be sent to a notification. attachments : array of UNNotificationAttachment . Used to turn on audio, image or video.

Let’s create a notification request and for this we will declare a new class scheduleNotification

func scheduleNotification(notificationType: String) {



let content = UNMutableNotificationContent() // Содержимое уведомления



content.title = notificationType

content.body = "This is example how to create " + notificationType Notifications"

content.sound = UNNotificationSound.default

content.badge = 1

}

This method has the parameter notificationType, which will take the value of the selected string and display it in the notification header.

Notification Trigger

We filled the notification with content, now we need to create a trigger on which it should work. This can be based on a calendar, by time or by location.

Sending a calendar based notification

A trigger for a specific date and time is created using the date components object. To convert dates to date components, use the current calendar. For example:

let date = Date(timeIntervalSinceNow: 3600)

let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date)

Create a trigger from date components

let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)

To create a trigger that repeats at a certain interval, use the correct set of date components. For example, for the notification to be repeated every day at the same time, we only need hours, minutes, and seconds.

let date = Date(timeIntervalSinceNow: 3600)

let triggerDaily = Calendar.current.dateComponents([.hour,.minute,.second,], from: date)

let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)

To send notifications weekly at the same time, we need weekday days of the week

let date = Date(timeIntervalSinceNow: 3600)

let triggerWeekly = Calendar.current.dateComponents([.weekday, .hour, .minute, .second,], from: date)

let trigger = UNCalendarNotificationTrigger(dateMatching: triggerWeekly, repeats: true)

Send notification by location

This trigger is triggered at the moment when the user enters a specific location or vice versa leaves it. The location is determined by CoreLocation using CLRegion:

let trigger = UNLocationNotificationTrigger(triggerWithRegion: region, repeats:false)

Sending a time notification

We in our project will use this trigger. In the scheduleNotification(notificationType:) method, we create a tiger that will be configured for 5 seconds:

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

Schedule:

Having the content and trigger, we are ready to create a notification request and add it to the notification center. Each request requires a string identifier for further use:

let identifier = "Local Notification"

let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)



notificationCenter.add(request) { (error) in

if let error = error {

print("Error \(error.localizedDescription)")

}

}

If the application sends another notification with the same identifier, the first notification will be overridden by this new notification. Calling add() with the same identifier string will replace the existing notification. If you want to schedule multiple requests, use a different identifier each time.

So the scheduleNotification(notificationType:) method creates a notification request with specific content and on a specific schedule. It remains to call this method by tapping on the line with the type of notification on the main screen of the application.

Go to the TableViewController class and create an AppDelegate :

let appDelegate = UIApplication.shared.delegate as? AppDelegate

Go to the didSelectRowAt indexPath method and call scheduleNotification(notificationType:) in okAction closure.

let okAction = UIAlertAction(title: "OK", style: .default) { (action) in



self.appDelegate?.scheduleNotification(notificationType: notificationType)

}

After 5 seconds after clicking on the “OK” button, a notification will be sent and if the application at this point is not in the foreground, the user will receive it.

Now, after receiving the notification, the red badge with the number 1 is on the application icon. This badge does not disappear even after switching to the application. Let’s fix it and make it so that the badge disappears after opening the application

To do this, go to the AppDelegate class and enter the following line in the applicationDidBecomeActive method:

UIApplication.shared.applicationIconBadgeNumber = 0

We start the project, we check.

As mentioned above, we can receive a notification only when our application is either in the background or unloaded. In order to receive notifications even when the application is in the foreground, you need to implement the UNUserNotificationCenterDelegate protocol

extension AppDelegate: UNUserNotificationCenterDelegate {



}

This protocol contains two optional methods:

userNotificationCenter(_:willPresent:withCompletionHandler:) called when the application is in the foreground. We get a UNNotification , that contains a UNNotificationRequest request. In the method body, you must call the completion handler with a set of options for the UNNotificationPresentationOptions .

called when the application is in the foreground. We get a , that contains a UNNotificationRequest request. In the method body, you must call the completion handler with a set of options for the . userNotificationCenter (_: didReceive:withCompletionHandler:) is used to select an action for a notification. You get a UNNotificationResponse , that contains an actionIdentifier to determine which action to take. UNNotificationDefaultActionIdentifier and UNNotificationDismissActionIdentifier system identifiers are used when you need to open an application by tapping the notification or close the notification with a swipe.

In both cases, you need to call the completion handler to complete

To solve our problem, we implement the first method:

func userNotificationCenter(_ center: UNUserNotificationCenter,

willPresent notification: UNNotification,

withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {



}

We need to call the completionhandler to select a set of notification alert options.

completionHandler([.alert, .sound])

If you subscribe to the UNUserNotificationCenterDelegate protocol, you must assign a delegate. To do this, in the didFinishLaunchingWithOptions method, enter the following line:

notificationCenter.delegate = self

Now the user will be able to receive notifications, even if our application is in the foreground.

Notification processing

When a user clicks on a notification, he expects the application to open and perform some action. Now, when you tap the notification, the application opens automatically, but this action does not affect anything. If you need to respond to the notification, we must implement the second method of the UNUserNotificationCenterDelegate

func userNotificationCenter(_ center: UNUserNotificationCenter,

didReceive response: UNNotificationResponse,

withCompletionHandler completionHandler: @escaping () -> Void) {



if response.notification.request.identifier == "Local Notification" {

print("Handling notifications with the Local Notification Identifier")

}



completionHandler()

}

Local Notification with Action

To include custom actions for a user in a notification, you must first create and register notification categories. A category determines the type of notification that one or more actions can contain. As a practical example, create a category with two actions. For this we need to perform three main points:

1. Define the action (s) of the notification:

Go to the scheduleNotification(notificationType:) method and create a unique identifier and header. Notification action parameters may prompt the user:

unlock device

start the application by doing some action

provide a variant with a destructive effect, which will be highlighted in red.

let snoozeAction = UNNotificationAction(identifier: "Snooze", title: "Snooze", options: [])

let deleteAction = UNNotificationAction(identifier: "Delete", title: "Delete", options: [.destructive])

2. Creating a category for action

To create a new category, you need to define another unique identifier by which a specific category of actions will be determined:

let userActions = "User Actions"

Having the identifier, you can create an instance of the UNNotificationCategory class to initialize the category:

let category = UNNotificationCategory(identifier: userActions, actions: [snoozeAction, deleteAction], intentIdentifiers: [], options: [])

3. Category registration in the notification center

It remains to register the created category in the notification center.

notificationCenter.setNotificationCategories([category])

To include our actions in the notification, we must include the appropriate category in the content.

content.categoryIdentifier = userActions

As a result, our method now looks like this:

func scheduleNotification(notificationType: String) {



let content = UNMutableNotificationContent() // Содержимое уведомления

let userActions = "User Actions"



content.title = notificationType

content.body = "This is example how to create " + notificationType

content.sound = UNNotificationSound.default

content.badge = 1

content.categoryIdentifier = userActions



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

let identifier = "Local Notification"

let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)



notificationCenter.add(request) { (error) in

if let error = error {

print("Error \(error.localizedDescription)")

}

}



let snoozeAction = UNNotificationAction(identifier: "Snooze", title: "Snooze", options: [])

let deleteAction = UNNotificationAction(identifier: "Delete", title: "Delete", options: [.destructive])

let category = UNNotificationCategory(identifier: userActions, actions: [snoozeAction, deleteAction], intentIdentifiers: [], options: [])



notificationCenter.setNotificationCategories([category])

}

As a result, if you now pull the notification down, the list of possible actions will open:

You can display up to four actions.

Now the choice of an action does not affect anything. Let’s take a look at how to customize the behavior of the application depending on the choice. Moving to the second method of the UNUserNotificationCenterDelegate protocol and supplementing it with a switch statement:

switch response.actionIdentifier {

case UNNotificationDismissActionIdentifier:

print("Dismiss Action")

case UNNotificationDefaultActionIdentifier:

print("Default")

case "Snooze":

print("Snooze")

scheduleNotification(notificationType: "Reminder")

case "Delete":

print("Delete")

default:

print("Unknown action")

}

Refactoring

We described all the logic of our notifications in the AppDelegate class, but it would be preferable to create a separate class for them. Add a new Notifications file to the project and create a new class in it:

import Foundation

import UserNotifications



class Notifications: NSObject, UNUserNotificationCenterDelegate {



}

We transfer all the logic of our notifications from the AppDelegate class to the Notifications . class. To fulfill the request for permission to send notifications, create the method notificationRequest

func notificationRequest() {



let options: UNAuthorizationOptions = [.alert, .sound, .badge]



notificationCenter.requestAuthorization(options: options) {

(didAllow, error) in

if !didAllow {

print("User has declined notifications")

}

}

}

It is important to assign a notification delegate at the very beginning of the application’s life cycle, for example, in the AppDelegate class’s didFinishLaunchingWithOptions method:

let notifications = Notifications()



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {



notifications.notificationCenter.delegate = notifications

notifications.notificationRequest()



return true

}

Go to the TableViewController class and instead of the appDelegate property, create a new one:

let notifications = Notifications()

We also make the appropriate changes to the didSelectRowAt indexPath: :

let okAction = UIAlertAction(title: "OK", style: .default) { (action) in



self.notifications.scheduleNotification(notificationType: notificationType)

}

The final project can be downloaded here.