Implementing Local Notifications in iOS 8 (Swift)

What are notifications in iOS and why do we need them?

Notifications in iOS allows the application to notify users about any event/action that requires user’s attention even when not using the application. For instance, if you need to remind the user of something at a later time/date when the user might not necessarily be using the application or even if the phone is locked, notifications are the way to go.

What are Remote and Local Notifications?

Remote notifications are useful when the event is triggered by a remote server with which the application interacts with and it requires an internet connection. On the other hand, local notification is useful where some event is triggered locally by the application. Local notifications doesn’t require an internet connection.

How to Implement Local Notification?

To start with this, let us consider a case where the user wants to create a reminder. Let’s say an application that allows users to create a reminder with a title, details and a time when he/she wants to be reminded. As shown in the image below. The application at the specified time, notifies the user with the details of the remainder.

iRemind_

Implementation:

1. An application to add notification:

For this demo, I have created an application which has the below two screens. (You may consider any of your existing application that suits the purpose.)

  • Reminder List: To display the list of created reminders.
  • Reminder Info: To create new or edit an existing reminder.

(Check the sample code provided at the end.)

2. Registering for Local Notifications:

Local notifications are scheduled from the application and a specific date, time is set for iOS to fire the notification. iOS prior to firing the notification checks if the user has enabled notifications for our application.
Then Yes! first step is to get the consent of the user. For this we need to specify the types of notification that we wish to post from the application and register it with the UIApplication class. The iOS in turn throws an alert to the user asking for his/her consent.

Registering for Simple Notification:

        // Register for notification: This will prompt for the user's consent to receive notifications from this app.
        let notificationSettings = UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Sound | UIUserNotificationType.Badge, categories: nil)
        //*NOTE*
        // Registering UIUserNotificationSettings more than once results in previous settings being overwritten.
        UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)

In the above code we are specifying that the app will provide with an Alert, a Badge on the App Icon and a Notification Sound.
For a simple notification without any actions we can set categories: nil.

For providing quick action in the notifications such as those below. (Considering a reminder app where you want to show two options: Confirm and Snooze in the notification itself, and without having to open up the application). We need to create Notification Actions, add them to a Notification Category and set it while registering for the Notification.

iRemind_2 iRemind_1

Registering for Actionable Notification:

a) Creating Notification Actions:

        // Specify the notification actions.
        let reminderActionConfirm = UIMutableUserNotificationAction()
        reminderActionConfirm.identifier = "Confirm"
        reminderActionConfirm.title = "Confirm"
        reminderActionConfirm.activationMode = UIUserNotificationActivationMode.Background
        reminderActionConfirm.destructive = false
        reminderActionConfirm.authenticationRequired = false
        
        let reminderActionSnooze = UIMutableUserNotificationAction()
        reminderActionSnooze.identifier = "Snooze"
        reminderActionSnooze.title = "Snooze"
        reminderActionSnooze.activationMode = UIUserNotificationActivationMode.Background
        reminderActionSnooze.destructive = true
        reminderActionSnooze.authenticationRequired = false
  • identifier: a string used as key to identify the action if user clicks on the specific button.
  • title: title for the button used for the action.
  • activationMode: When this action should be displayed. Background indicates this action is shown to the user when the app is not in use/in background.
  • destructive: Setting this parameter “true” shows the button in RED to indicate destructive action in the notification centre.
  • authenticationRequired: Set this parameter to “true” if you want the user to unlock the phone (if locked) for performing this action. (prevents unauthorised users from performing this action if it is potentially destructive)

iRemind_3

b) Adding Notification Actions to a Notification Category:

        // Create a category with the above actions
        let shoppingListReminderCategory = UIMutableUserNotificationCategory()
        shoppingListReminderCategory.identifier = "reminderCategory"
        shoppingListReminderCategory.setActions([reminderActionConfirm, reminderActionSnooze], forContext: UIUserNotificationActionContext.Default)
        shoppingListReminderCategory.setActions([reminderActionConfirm, reminderActionSnooze], forContext: UIUserNotificationActionContext.Minimal)
  • identifier: identifier for the category
  • setActions: set the created actions for Default, Minimal context. minimal context used for notification banner should have minimum buttons. Default actions for displaying in alerts views that can have more buttons.

c) Register for Notification with UIApplication:

        // Register for notification: This will prompt for the user's consent to receive notifications from this app.
        let notificationSettings = UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Sound | UIUserNotificationType.Badge, categories: Set(arrayLiteral: shoppingListReminderCategory))
        //*NOTE*
        // Registering UIUserNotificationSettings more than once results in previous settings being overwritten.
        UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
  • UIUserNotificationSettings: Create Notification settings with notification types and with the created categories.
    * (Multiple categories can be specified for notification registration and the appropriate category is specified while scheduling the notification instance for using the actions specified for the categories)
  • registerUserNotificationSettings: call this method by passing the notification settings on the sharedApplication instance of UIApplication.

Registering for notification can be done at any point in time. For an application with Notifications as the main feature, it is just appropriate to register when the application is launched. So we will add the above piece of code in the delegate method application: willFinishLaunchingWithOptions: which gets called when the app is launched.

func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 

If you are familiar with application: didFinishLaunchingWithOptions: function, it is not recommended for newer iOS versions.

IMPORTANT
For app initialization, it is highly recommended that you use this method and the application:willFinishLaunchingWithOptions: method and do not use the applicationDidFinishLaunching: method, which is intended only for apps that run on older versions of iOS.

Apple: UIApplicationDelegate Protocol Reference

iOS displays an alert as shown below for getting the consent of user for allowing the application to send notifications.

iRemind_0a

This selection can be viewed/changed by the user by navigating to: Settings App -> (App Name) -> Notifications which looks like below.

iRemind_0b

3. Scheduling Local Notifications:

Scheduling notification is simple. Create a notification instance and specify the required parameters. Then schedule the notification on the UIApplication.sharedApplication instance.

    func scheduleLocalNotification() {
        // Create reminder by setting a local notification
        let localNotification = UILocalNotification() // Creating an instance of the notification.
        localNotification.alertTitle = "Notification Title"
        localNotification.alertBody = "Alert body to provide more details"
        localNotification.alertAction = "ShowDetails"
        localNotification.fireDate = NSDate().dateByAddingTimeInterval(60*5) // 5 minutes(60 sec * 5) from now
        localNotification.timeZone = NSTimeZone.defaultTimeZone()
        localNotification.soundName = UILocalNotificationDefaultSoundName // Use the default notification tone/ specify a file in the application bundle
        localNotification.applicationIconBadgeNumber = 1 // Badge number to set on the application Icon.
        localNotification.category = "reminderCategory" // Category to use the specified actions
        UIApplication.sharedApplication().scheduleLocalNotification(localNotification) // Scheduling the notification.
    }

Create a UILocalNotification Instance. Specify following parameters:

  • alertTitle: Used as the title while displaying the notification as alert or banner.
  • alertBody: Used as the description label while displaying the notification as alert or banner.
  • alertAction: is the action identifier used while handling the action in the delegate method: application: handleActionWithIdentifier: forLocalNotification: completionHandler:
  • fireDate: Date, Time for the notification to fire.
  • timeZone: Time Zone where user is in is what you would want to mostly use. Which is NSTimeZone.defaultTimeZone()
  • soundName: Specify the name of any sound file (.wav) in the application bundle or UILocalNotificationDefaultSoundName for the default sound.
  • applicationIconBadgeNumber: Specifies the badge number on the application icon when the notification appears.
  • category: Identifier of the category previously defined. The actions associated with the categories will be added to this notification automatically.
  • scheduleLocalNotification: Pass the created notification to schedule it.

4. Handling notification and notification actions:

a) Resetting application Badge count:
When the application is launched after a notification is fired means the notification is viewed. So we need to clear the badge on the app Icon. The applicationDidBecomeActive: method would be the ideal place to do that.

    func applicationDidBecomeActive(application: UIApplication) {
        // Reset the application badge to zero when the application as launched. The notification is viewed.
        if application.applicationIconBadgeNumber > 0 {
            application.applicationIconBadgeNumber = 0
        }
    }

b) Handling Notification when the app is open.
Even when the app is active, if the notification is fired, we might need to show an alert or refresh a view or update some data. These can be handled in the delegate method application: didReceiveLocalNotification: This method also gets called when the user taps on the notification to wake the application from background.

    func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
        // Point for handling the local notification when the app is open.
        // Showing reminder details in an alertview
        UIAlertView(title: notification.alertTitle, message: notification.alertBody, delegate: nil, cancelButtonTitle: "OK").show()
    }

c) Handling Notification after the app is killed from background.
If the application is currently exited/killed and the user opens the application by tapping on the notification, the required action will have to be handled in the delegate method application:willFinishLaunchingWithOptions: as described below.

If the user chooses to open the app when a local notification occurs, the launch options dictionary passed to the application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods contains the UIApplicationLaunchOptionsLocalNotificationKey key. This method is called at some point after your delegate’s application:didFinishLaunchingWithOptions: method.

Apple: UIApplicationDelegateProtocol

Which leaves us with an implementation like below.

    func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
	// ..
	// ..
	// ..
        // Handle any action if the user opens the application throught the notification. i.e., by clicking on the notification when the application is killed/ removed from background.
        if let aLaunchOptions = launchOptions { // Checking if there are any launch options.
            // Check if there are any local notification objects.
            if let notification = (aLaunchOptions as NSDictionary).objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") as? UILocalNotification {
                // Handle the notification action on opening. Like updating a table or showing an alert.
                UIAlertView(title: notification.alertTitle, message: notification.alertBody, delegate: nil, cancelButtonTitle: "OK").show()
            }
        }
        return true
    }

d) Handling Notification Actions:
Notification actions are handled in the delegate callback application: handleActionWithIdentifier: forLocalNotification: completionHandler: A sample implementation for showing alert/a snooze action is as below. The appropriate action can e handled based on the identifier specified while creating the particular action.

    func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
        // Point for handling the local notification Action. Provided alongside creating the notification.
        if identifier == "ShowDetails" {
            // Showing reminder details in an alertview
            UIAlertView(title: notification.alertTitle, message: notification.alertBody, delegate: nil, cancelButtonTitle: "OK").show()
        } else if identifier == "Snooze" {
            // Snooze the reminder for 5 minutes
            notification.fireDate = NSDate().dateByAddingTimeInterval(60*5)
            UIApplication.sharedApplication().scheduleLocalNotification(notification)
        } else if identifier == "Confirm" {
            // Confirmed the reminder. Mark the reminder as complete maybe?
        }
        completionHandler()
    }

5. Testing Local Notifications:

Local notifications can be tested in Simulator as well as devices. So go ahead. Add notifications to your application and test it! 🙂

iRemind_4
Notification when App is Active
iRemind_5
Notification when Device is Locked
iRemind_5a
Notification Badge on App Icon
iRemind_5b
Notification Banner
iRemind_5c
Notification in Notification Center

Here’s the sample code of my reminder application called iRemind! 😉

Thanks for reading!

References:

  1. Apple: UIApplicationDelegate Protocol Reference
  2. Apple: Registering, Scheduling, and Handling User Notifications
  3. Apple: Local and Remote Notifications in Depth

4 thoughts on “Implementing Local Notifications in iOS 8 (Swift)

  1. Hi, thanks for sharing the code and the project. I am having some trouble although running the app in the simulator. without changing anything in the code i get 5 errors and some warnings about the code in the ReminderListTableVC.swift like “Redundant conformance of ‘ReminderListTableVC’ to protocol ‘UITableViewDataSource'” or “Downcast from ‘UITableViewCell?’ to ‘UITableViewCell’ only unwraps optionals” and i don’t know what to do. any ideas?
    I am new and i am learning how to work with reminders and notifications.
    thanks in advance

    Like

  2. I found the first error doesn’t need the “as! UITableViewCell”
    the correct it would be
    let cell = tableView.dequeueReusableCellWithIdentifier(“ReminderInfo”, forIndexPath: indexPath)

    two more to solve
    if count(self.reminderList) > 0 {
    //’count’ is unavailable: access the ‘count’ property on the collection

    class ReminderListTableVC: UITableViewController, UITableViewDataSource, UITableViewDelegate {
    // Redundant conformance of ‘ReminderListTableVC’ to protocol ‘UITableViewDataSource’

    Like

Leave a comment