Think And Build

iOS

Interactive Notifications with Notification Actions

Posted on .

Interactive Notifications with Notification Actions

Introduction

iOS 8 comes with a long list of interesting improvements and in this article I want to show you one of those: Notification Actions.
With this new feature users can directly interact with push or local notifications without opening the application. A simple example? A todo app presents a reminder of a task to the user through a local notification. We can now easily add a button to the notification message to mark the task as done directly within the notification banner/alert.

Let’s Code

To show you how Notification Actions works I’ve implemented a simple counter application that shows a message every 60 seconds, asking the user to increment or decrement the counter value. It’s a useful app, isn’t it?

To keep it simple, this project works with local notifications only, but all these steps can be easily adapted to Remote notifications. From now on, keep in mind that every time I speak about notifications I mean both local and push notifications.

So download the app source code and let’s start looking at the AppDelegate.swift file.

Actions, Categories and Settings

With iOS 8 we add new information to notifications:

Actions: they describe the actions that the user can perform to interact with the notification message. They are presented as buttons. They are defined by the UIUserNotificationAction class.

Categories: we group actions into categories and for any notification we can set a specific category. They are defined by the UIUserNotificationCategory class.

Settings: they need to be registered by the application and they contain all the categories that we create. Settings are defined by the UIUserNotificationSettings class.

With the counter app we need three actions:
Increment (add 1), Decrement (subtract 1) and Reset (set the counter to 0). To create the needed UIUserNotificationAction(s) we use this code:


        // 1. Create the actions **************************************************
        
        // increment Action
        let incrementAction = UIMutableUserNotificationAction()
        incrementAction.identifier = "INCREMENT_ACTION"
        incrementAction.title = "Add +1"
        incrementAction.activationMode = UIUserNotificationActivationMode.Background
        incrementAction.authenticationRequired = true
        incrementAction.destructive = false

        // decrement Action
        let decrementAction = UIMutableUserNotificationAction()
        decrementAction.identifier = c
        decrementAction.title = "Sub -1"
        decrementAction.activationMode = UIUserNotificationActivationMode.Background
        decrementAction.authenticationRequired = true
        decrementAction.destructive = false
        
        // reset Action
        let resetAction = UIMutableUserNotificationAction()
        resetAction.identifier = “RESET_ACTION"
        resetAction.title = "Reset"
        resetAction.activationMode = UIUserNotificationActivationMode.Foreground
        // NOT USED resetAction.authenticationRequired = true
        resetAction.destructive = true

UIUserNotificationAction instances are made of a series of elements.

identifier: this string is a unique id that we’ll use later to identify the action chosen by the user.

title: this is just the title of the button for the action.

activationMode: we have two options: foreground or background. If your action needs to show the application interface you choose foreground, otherwise choose background and the action will take place without launching the application.

authenticationRequired: this parameter is used only if you set background as activation mode. In this case, setting authentication required to true, the user needs to insert the unlock code to launch the action in background.

destructive: if it is set to true the action button has a red background (useful for “risky” operations).

Now that we have a list of actions we need to enclose them into a UIUserNotificationCategory and then store it into a UIUserNotificationSettings.

The code is extremely simple and starts with the creation of the category:


        // 2. Create the category ***********************************************
        
        // Category
        let counterCategory = UIMutableUserNotificationCategory()
        counterCategory.identifier = “COUNTER_CATEGORY”
        
        // A. Set actions for the default context
        counterCategory.setActions([incrementAction, decrementAction, resetAction],
            forContext: UIUserNotificationActionContext.Default)
        
        // B. Set actions for the minimal context
        counterCategory.setActions([incrementAction, decrementAction],
            forContext: UIUserNotificationActionContext.Minimal)

We set the category identifier because it is needed to recognise the category for each action. Then we store the previous actions into the category using the setActions:forContext: method. As you can see we set two different contexts, default and minimal.

The minimal context refers to the notification banner, the lock screen device view and the pull down notifications area. In these cases the notifications actions that can be shown are no more then two.

While the default context is only available when the user accepts to show notifications for your app as alerts, in this case we get all the actions that we have set.

At this point we can build the settings using this code:

        
        // 3. Notification Registration *****************************************
        
        let types = UIUserNotificationType.Alert | UIUserNotificationType.Sound
        let settings = UIUserNotificationSettings(forTypes: types, categories: NSSet(object: counterCategory))
        UIApplication.sharedApplication().registerUserNotificationSettings(settings)

We choose the type of notification that the application needs and we build the settings passing these types and all the created categories (in our case just one).

The last operation is to call the function registerUserNotificationSettings on the shared application.

When this code is called for the first time, the user will be prompted with an alert that asks for permissions to show the notifications, exactly as we were used to with notifications in iOS 7.

Schedule the notifications

The code that we use to schedule the Interactive Notifications is exactly the same that we write for standard notifications with the addition of a property to point to the category:


            let notification = UILocalNotification()
            notification.alertBody = "Hey! Update your counter ;)"
            notification.soundName = UILocalNotificationDefaultSoundName
            notification.fireDate = NSDate()
            notification.category = “COUNTER_CATEGORY”
            notification.repeatInterval = NSCalendarUnit.CalendarUnitMinute

            
UIApplication.sharedApplication().scheduleLocalNotification(notification)

Notice that the category property stores the same value we have previously set as category identifier (“COUNTER_CATEGORY”). So now we are sure that when this notification is fired it will present the actions stored in the category with id COUNTER_CATEGORY.

How to handle the Actions

So this application launches a notification with 3 actions every minute. If the application is not in foreground the user will be prompted with the notification alert and she can choose one of the 2 (minimal context) or 3 (default context) actions available.

What we need now is a way to perform some operations following the user choices. The function application:handleActionWithIdentifier:forLocalNotification:completionHandler is exactly what we need (there is a similar function for remote notifications too).

Easily enough, this function passes the identifier of the action chosen by the user. When we set the actions we used three identifiers: INCREMENT_ACTION, DECREMENT_ACTION and RESET_ACTION. We can also access the notification.category to be sure that it matches the identifier we set for the category (COUNTER_CATEGORY).

The completionHandler is a closure that you need to call when all the handling operation are done.

Here is the code:


    func application(application: UIApplication,
        handleActionWithIdentifier identifier: String?,
        forLocalNotification notification: UILocalNotification,
        completionHandler: () -> Void) {
        
        // Handle notification action *****************************************
        if notification.category == "COUNTER_CATEGORY" {
            
            let action:Actions = Actions.fromRaw(identifier!)!
            let counter = Counter();
            
            switch action{
                
                case "INCREMENT_ACTION":
                counter++
                
                case "DECREMENT_ACTION":
                counter--
                
                case "RESET_ACTION":
                counter.currentTotal = 0

            }
        }
        
        completionHandler()
    }

The Counter class is a class that I’ve built as model just to manage the current value of the counter (you can check its code in the Counter.swift file) as you can see we just increment, decrement or reset the counter depending on the action that the user has selected.

Note: the project code is slightly different because I’ve encapsulated the identifiers strings within an enum. For clarity’s sake, during this article I’ve used their string values directly.

That’s it. 😉 You can now install and launch the application on your device and be annoyed every 60 seconds by this outstanding piece of software!

Let’s discuss the tutorial on Twitter!

Yari D'areglia

Yari D'areglia

https://www.thinkandbuild.it

Senior iOS developer @ Neato Robotics by day, game developer and wannabe artist @ Black Robot Games by night.

Navigation