Actions in Push Notifications

In last week’s tutorial, I discussed how to set up a push notification. I also showed you how to test a notification on a test platform with a JSON payload. This week, we’ll go tot the next level: using actions in a push notification. We’ll start here from where we left off last week. If you have not done that lesson, go there first. Because of its nature there is no download file. As I do there, I’m assuming you know how to make a local notification. If you do not, you might want to read this first.

No More Blank Screens

Before we get started, I missed something in the last lesson I should have done. Go to the storyboard, and add one label “Push Pizza Company.”
2017-01-09_03-59-42

I centered this label using the auto layout align function, setting update constraint to items of new constraints.

2017-01-09_03-33-58
This give us one more visual cue the app ran correctly.

Payload Changes

The beauty of the User Notification framework is actions for a push notification work almost the same as a local notification. You set a category in the content, this time in the payload. You add one more key to the aps dictionary for the category:

{
     "aps":{
          "alert":{
              "title":"Push Pizza Co.",
              "body":"Your pizza is almost ready!"
           },
           "badge":42,
           "sound":"default",
           "category":"pizza.category"
    }
}

Setting Categories and Actions

Then you set the category in the AppDelegate. I usually create a new function to do this. Add this function:

func setCategories(){

}

Your first step in this function is to make an action using the UNNotificationAction constructor:

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

The action wants a unique identifier, which you’ll use to identify it when an action fires. The method also wants a title for the button that will appear on the notification for the action. We have no options, so leave that as a blank array.

Next we stick the action in a category, I’m using the same category identifier as the payload category.

    let pizzaCategory = UNNotificationCategory(
        identifier: "pizza.category",
        actions: [snoozeAction],
        intentIdentifiers: [],
        options: [])

Notice actions is an array. I have only one here, but you can add several if you wanted to. Finally I send the category to the system

    NUserNotificationCenter.current().setNotificationCategories(
        [pizzaCategory])
}

With these three lines You’ve created the snoozeAction, made a category pizza.catergory with them, and then set the notification into the notification center. This had to one of the first thing you do when the application launches, so call the function on didFinishLoading

setCategories()

Build and run. When the push pizza company label appears, on the app, you can close it. Launch the notification on the test platform. (for setup of the test platform see last week’s post) You’ll see the notification, then view it.

img_0023

We have the snooze button, but it does nothing.

Running Code from Actions

In order to make the snooze button do something, You need a delegate method found in the UNNotificationCenterDelegate delegate. Adopt the delegate

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

In didfinishLoading, Set the delegate to self.

UNUserNotificationCenter.current().delegate = self

Add the didRecieveResonse delegate method to the class.

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

}

By the time the notification reached this method, the payload from the push notifications is now content of a notification. For a snooze button I don’t touch the content, but I can copy it to a new local notification. Create three constants action, request, and content to have these easily available.

let action = response.actionIdentifier
let request = response.notification.request
let content = request.content

You identify the actionIdentifier then run code if it matches. Add this code for when the action is a snooze.action.

if action == "snooze.action"{
    let snoozeTrigger = UNTimeIntervalNotificationTrigger(
        timeInterval: 5.0,
        repeats: false)
    let snoozeRequest = UNNotificationRequest(
        identifier: "pizza.snooze",
        content: content,
        trigger: snoozeTrigger)
     center.add(snoozeRequest){
        (error) in
        if error != nil {
            print("Snooze Request Error: \(error?.localizedDescription)")
        }
     }
}

Our code will convert the remote notification to a local time interval notification. I’ll make a new trigger with a time interval of 5 seconds and non-repeating. I made new Notification request snoozeRequest using the identifier from the push notification, the content from the push notification, and the new snooze trigger. Finally, I added the notification to the Notification Center associated with the notification.

In the request identifier, I used a single literal identifier so any push notifications using this action will have only one snooze notification, updating the content of the last snooze. You won’t have fifty snoozes for fifty different pushes.

Finally, make sure you add the completion handler at the end of the delegate method.

completionHandler()

Run the application to load all these changes onto your device. Stop the app once it runs. Go to the test platform and send a notification.

2017-01-09_04-40-40

You’ll see the notification appear on the device. Open the notification and hit Snooze.

img_0023

Five seconds later, the notification appears again.

In this example I used a snooze button and made it a local notification from a push notification. If you have notifications that will repeat after the initial push notification, this is a very good practice. You have a limited number of push notification per app per day. Instead of squandering them on repeating the same message, use a local notification for repetitive notifications like snooze buttons. Also remember you only have four actions. Don’t go crazy adding actions everywhere.

The Whole Code

Like last week there is no download. There so little here you can copy it, and much of the meat is the certificates which you have to do yourself. Fr brevity, I removed extra unused methods from the app delegate.

//
//  AppDelegate.swift
//  PushNotification
//
//  Created by Steven Lipton on 12/30/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    var window: UIWindow?

    func setCategories(){
        let snoozeAction = UNNotificationAction(
            identifier: "snooze.action",
            title: "Snooze",
            options: [])
        let pizzaCategory = UNNotificationCategory(
            identifier: "pizza.category",
            actions: [snoozeAction],
            intentIdentifiers: [],
            options: [])
        UNUserNotificationCenter.current().setNotificationCategories(
            [pizzaCategory])
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        setCategories()
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge], completionHandler: {(granted,error) in
            if granted{
                application.registerForRemoteNotifications()
            }
        })
        
        return true
    }
    
    
    func tokenString(_ deviceToken:Data) -> String{
        //code to make a token string
        let bytes = [UInt8](deviceToken)
        var token = ""
        for byte in bytes{
            token += String(format: "%02x",byte)
        }
        return token
    }
    
    
    //MARK: - Register Handling
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print ("token -- \n \(tokenString(deviceToken))")
    }
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Error = \(error.localizedDescription)")
    }

    //MARK: - Delegates for Notifications
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let action = response.actionIdentifier
        let request = response.notification.request
        let content = request.content
        if action == "snooze.action"{
            let snoozeTrigger = UNTimeIntervalNotificationTrigger(
                timeInterval: 5.0,
                repeats: false)
            let snoozeRequest = UNNotificationRequest(
                identifier: "pizza.snooze",
                content: content,
                trigger: snoozeTrigger)
            center.add(snoozeRequest){
                (error) in
                if error != nil {
                    print("Snooze Request Error: \(error?.localizedDescription)")
                }
            }
        }
        completionHandler()
    }
}


One Reply to “Actions in Push Notifications”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s