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.”
I centered this label using the auto layout align function, setting update constraint to items of new constraints.
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.
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.
You’ll see the notification appear on the device. Open the notification and hit Snooze.
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() } }
Leave a Reply