Local Notifications with iOS 10
I can’t believe it is over six years since I first wrote about local notifications in iOS 4. Well in iOS 10 Apple has deprecated
UILocalNotification
which means it is time to get familiar with a new notifications framework.Setup
This is a long post so let’s start easy by importing the new notifications framework:
// Swift
import UserNotifications
// Objective-C (with modules enabled)
@import UserNotifications;
You manage notifications through a shared
UNUserNotificationCenter
object:// Swift
let center = UNUserNotificationCenter.current()
// Objective-C
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
Authorization
As with the older notifications framework you need to have the user’s permission for the types of notification your App will use. Make the request early in your App life cycle such as in
application:didFinishLaunchingWithOptions:
. The first time your App requests authorization the system shows the user an alert, after that they can manage the permissions from settings:There are four notification types,
badge
, sound
, alert
, carPlay
you can combine as required. For example if you want both alerts and sound:// Swift
let options: UNAuthorizationOptions = [.alert, .sound];
// Objective-C
UNAuthorizationOptions options = UNAuthorizationOptionAlert + UNAuthorizationOptionSound;
You make the actual authorization request using the shared notification center:
// Swift
center.requestAuthorization(options: options) {
(granted, error) in
if !granted {
print("Something went wrong")
}
}
// Objective-C
[center requestAuthorizationWithOptions:options
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!granted) {
NSLog(@"Something went wrong");
}
}];
The framework calls the completion handler with a boolean indicating if the access was granted and an error object which will be
nil
if no error occurred.
Note: The user can change the notifications settings for your App at any time. You can check the allowed settings with
getNotificationSettings
. This calls a completion block asynchronously with a UNNotificationSettings
object you can use to check the authorization status or the individual notification settings:// Swift
center.getNotificationSettings { (settings) in
if settings.authorizationStatus != .authorized {
// Notifications not allowed
}
}
// Objective-C
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus != UNAuthorizationStatusAuthorized) {
// Notifications not allowed
}
}];
Creating A Notification Request
A
UNNotificationRequest
notification request contains content and a trigger condition:Notification Content
The content of a notification is an instance of the
UNMutableNotificationContent
with the following properties set as required:title
: String containing the primary reason for the alert.subtitle
: String containing an alert subtitle (if required)body
: String containing the alert message textbadge
: Number to show on the app’s icon.sound
: A sound to play when the alert is delivered. UseUNNotificationSound.default()
or create a custom sound from a file.launchImageName
: name of a launch image to use if your app is launched in response to a notification.userInfo
: A dictionary of custom info to pass in the notificationattachments
: An array ofUNNotificationAttachment
objects. Use to include audio, image or video content.
Note that when localizing the alert strings like the title it is better to use
localizedUserNotificationString(forKey:arguments:)
which delays loading the localization until the notification is delivered.
A quick example:
// Swift
let content = UNMutableNotificationContent()
content.title = "Don't forget"
content.body = "Buy some milk"
content.sound = UNNotificationSound.default()
// Objective-C
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
content.title = @"Don't forget";
content.body = @"Buy some milk";
content.sound = [UNNotificationSound defaultSound];
Notification Trigger
Trigger a notification based on time, calendar or location. The trigger can be repeating:
- Time interval: Schedule a notification for a number of seconds later. For example to trigger in five minutes:
// Swift let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 300, repeats: false) // Objective-C UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:300 repeats:NO];
- Calendar: Trigger at a specific date and time. The trigger is created using a date components object which makes it easier for certain repeating intervals. To convert a
Date
to its date components use the current calendar. For example:// Swift let date = Date(timeIntervalSinceNow: 3600) let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date) // Objective-C NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3600]; NSDateComponents *triggerDate = [[NSCalendar currentCalendar] components:NSCalendarUnitYear + NSCalendarUnitMonth + NSCalendarUnitDay + NSCalendarUnitHour + NSCalendarUnitMinute + NSCalendarUnitSecond fromDate:date];
To create the trigger from the date components:// Swift let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false) // Objective-C UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDate repeats:NO];
To create a trigger that repeats at a certain interval use the correct set of date components. For example, to have the notification repeat daily at the same time we need just the hour, minutes and seconds:let triggerDaily = Calendar.current.dateComponents([hour,.minute,.second,], from: date) let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
To have it repeat weekly at the same time we also need the weekday:let triggerWeekly = Calendar.current.dateComponents([.weekday,hour,.minute,.second,], from: date) let trigger = UNCalendarNotificationTrigger(dateMatching: triggerWeekly, repeats: true)
This is an interesting change from the older notification framework which had a fire date and repeat interval. The date components are more flexible but there is no easy way to fully replicate thefireDate
property of the older framework. For example, if I want a daily trigger that starts in 1 week. I will look at some workarounds in a future post. - Location: Trigger when a user enters or leaves a geographic region. The region is specified through a CoreLocation
CLRegion
:// Swift let trigger = UNLocationNotificationTrigger(triggerWithRegion:region, repeats:false) // Objective-C UNLocationNotificationTrigger *locTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:NO];
Scheduling
With both the content and trigger ready we create a new notification request and add it to the notification center. Each notification request requires a string identifier for future reference:
// Swift
let identifier = "UYLLocalNotification"
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if let error = error {
// Something went wrong
}
})
// Objective-C
NSString *identifier = @"UYLLocalNotification";
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
content:content trigger:trigger]
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Something went wrong: %@",error);
}
}];
Note: Calling
addNotificationRequest
with the same identifier string will replace the existing notification. If you want to schedule multiple requests use a different identifier each time.Custom Actions
To include custom actions for the user in a notification you first need to create and register notification categories. A category defines a type of notification that can have one or more actions. As a practical example, let’s create a category with two actions. There are three key steps:
- Define the notification action(s): This requires a unique identifier and a (preferably localized) title. The notification action options allow you to require the user to unlock the device, to indicate a destructive option or to launch the app in the foreground.
// Swift let snoozeAction = UNNotificationAction(identifier: "Snooze", title: "Snooze", options: []) let deleteAction = UNNotificationAction(identifier: "UYLDeleteAction", title: "Delete", options: [.destructive]) // Objective-C UNNotificationAction *snoozeAction = [UNNotificationAction actionWithIdentifier:@"Snooze" title:@"Snooze" options:UNNotificationActionOptionNone]; UNNotificationAction *deleteAction = [UNNotificationAction actionWithIdentifier:@"Delete" title:@"Delete" options:UNNotificationActionOptionDestructive];
I have not shown an example but you can also create a text input action. SeeUNTextInputNotificationAction
for details. - Create a category with the actions: This requires another unique identifier (you probably want to define these magic strings in an enum):
// Swift let category = UNNotificationCategory(identifier: "UYLReminderCategory", actions: [snoozeAction,deleteAction], intentIdentifiers: [], options: []) // Objective-C UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"UYLReminderCategory" actions:@[snoozeAction,deleteAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; NSSet *categories = [NSSet setWithObject:category];
- Register the category with the notification center. It is recommended to do this early in the app lifecycle.
// Swift center.setNotificationCategories([category]) // Objective-C [center setNotificationCategories:categories];
To include this action in our notifications we need to set the category in the notification content:
// Swift
content.category = "UYLReminderCategory"
// Objective-C
content.category = @"UYLReminderCategory";
The custom action now appears as part of the user notification:
You can display up to four actions but depending on screen space the user may not see them all. We will look in the next section at how to respond when the user selects the custom action.
The Notification Delegate
If you want to respond to actionable notifications or receive notifications while your app is in the foreground you need to implement the
UNUserNotificationCenterDelegate
. This protocol defines two optional methods:userNotificationCenter(_:willPresent:withCompletionHandler:)
is called when a notification is delivered to a foreground app. You receive theUNNotification
object which contains the originalUNNotificationRequest
. You call the completion handler with theUNNotificationPresentationOptions
you want to present (use .none to ignore the alert).userNotificationCenter(_:didReceive:withCompletionHandler:)
is called when a user selects an action in a delivered notification. You receive theUNNotificationResponse
object which includes theactionIdentifier
for the user action and theUNNotification
object. The system defined identifiersUNNotificationDefaultActionIdentifier
andUNNotificationDismissActionIdentifier
are used when the user taps the notification to open the app or swipes to dismiss the notification.
In both cases you must call the completion handler once you finish.
You could use the app delegate but I prefer to create a separate class. Here is what a minimal notification delegate could like:
class UYLNotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Play sound and show alert to the user
completionHandler([.alert,.sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
// Determine the user action
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
print("Dismiss Action")
case UNNotificationDefaultActionIdentifier:
print("Default")
case "Snooze":
print("Snooze")
case "Delete":
print("Delete")
default:
print("Unknown action")
}
completionHandler()
}
}
It is important to set the delegate before your app finishes launching. For example, in the application delegate method
didFinishLaunchingWithOptions
:// Do NOT forget to retain your delegate somewhere
let notificationDelegate = UYLNotificationDelegate()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = notificationDelegate
// ...
return true
}
Managing Pending and Delivered
The old
UILocalNotification
framework always allowed you to remove individual or all pending notifications. The new UserNotifications
framework greatly extends that by allowing you to manage both pending requests and delivered notifications that are still displayed in Notification Center. There are three similar methods for each:getPendingNotificationRequests:completionHandler:
getDeliveredNotificationRequests:completionHandler:
Both methods return an array of objects in the completion handler. For pending requests you get an array of
UNNotificationRequest
objects. For delivered notifications you get an array of UNNotification
objects which contain the original UNNotificationRequest
and the delivery date.removePendingNotificationRequests:withIdentifiers:
removeDeliveredNotifications:withIdentifiers:
Removes pending requests or delivered notifications. You pass these methods an array of the string identifiers used to schedule the notifications. If you first retrieve the array of pending requests or delivered notifications the identifier is a property of the
UNNotificationRequest
object.removeAllPendingNotificationRequests
removeAllDeliveredNotifications
As the name suggests these methods remove all pending requests or delivered notifications.
You call these methods on the shared notification center. For example to remove all pending notification requests:
// Swift
center.removeAllPendingNotificationRequests()
// Objective-C
[center removeAllPendingNotificationRequests];
Further Reading
To read more and learn about some of the new features that I have not mentioned such as notification service extensions and notification content extensions take a look at the following WWDC sessions:
It is very useful information. Thank you for sharing this
ReplyDeleteiOS Course in Chennai | IOS Training in Chennai
The article is good.I got some knowledge about iOS.Thanks for sharing this blog.
ReplyDeleteiOS Training In Chennai | iOS Training Institute In Chennai
The Content was super and useful.Thankyou for posting this blog.I got some knowledge.
ReplyDeleteiOS Training In Chennai | iOS Training Institute In Chennai
I liked the way of presention.Its good and Informative.Thank you for posting this article
ReplyDeleteiOS Training In Chennai | iOS Training Institute In Chennai
This is an one of the excellent blog.I liked your way of presentation.I gained some Information.Thank you for posting this articleiOS Training In Chennai | iOS Training Institute In Chennai
ReplyDeleteThe Presentation of the article is good.I learned new topics.Thankyou for posting this blog
ReplyDeleteiOS Training In Chennai | iOS Training Institute In Chennai
The blog is more informative.Extraordinary and useful article.
ReplyDeleteiOS Training In Chennai | iOS Training Institute In Chennai
Thankyou for posting this article.I got clear idea.Its easy to understand and the presentation is good
ReplyDeleteiOS Training in Chennai | iOS Training Institute in Chennai
Wonderful Content!!! Which is very simple and useful. It helped me a lot in sharing a knowledge
ReplyDeleteIOS Training in Chennai
iOS Training Institutes in Chennai
Excellent information on iphone service center in chennai near me.I like the way of writing and presenting.keep sharing your ideas.
ReplyDeleteThanks for sharing this valuable information and we collected some information from this blog.
ReplyDeleteIOS Training in Noida
IOS Training institute in Noida
Great useful article. Most of the information is really helpful to me. Thanks for sharing this article.
ReplyDeleteIOS Care Center