Local Notifications with Swift 4

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

import UserNotifications
let notificationCenter = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .sound, .badge]
notificationCenter.requestAuthorization(options: options) {
(didAllow, error) in
if !didAllow {
print("User has declined notifications")
}
}
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:

  • subtitle: String data type for subheading notifications. (Used as needed).
  • body: A string containing the main text of the notification.
  • badge: Number to display on the application icon.
  • sound: Sound notification of delivery notification. To use the default sound, use UNNotificationSound.default().
  • launchImageName: 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.
  • attachments: array of UNNotificationAttachment. Used to turn on audio, image or video.
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
}

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)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
let date = Date(timeIntervalSinceNow: 3600)
let triggerDaily = Calendar.current.dateComponents([.hour,.minute,.second,], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
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:

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)")
}
}
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let okAction = UIAlertAction(title: "OK", style: .default) { (action) in

self.appDelegate?.scheduleNotification(notificationType: notificationType)
}
UIApplication.shared.applicationIconBadgeNumber = 0
extension AppDelegate: UNUserNotificationCenterDelegate {

}
  • 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) {

}
completionHandler([.alert, .sound])
notificationCenter.delegate = self

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:

  • 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"
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])
content.categoryIdentifier = userActions
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])
}
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 {

}
func notificationRequest() {

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

notificationCenter.requestAuthorization(options: options) {
(didAllow, error) in
if !didAllow {
print("User has declined notifications")
}
}
}
let notifications = Notifications()

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

notifications.notificationCenter.delegate = notifications
notifications.notificationRequest()

return true
}
let notifications = Notifications()
let okAction = UIAlertAction(title: "OK", style: .default) { (action) in

self.notifications.scheduleNotification(notificationType: notificationType)
}

Software Developer, Tech enthusiastic, iOS #swift \\ Android #kotlin

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store