How to deal with multiple scenarios with Push Notification in iOS

Issue #459

Here are my notes when working with Push Notification

How to register

Register to receive push notification

registerForRemoteNotificationTypes is deprecated in iOS 8+

1
UIApplication.sharedApplication().registerForRemoteNotifications()

Register to alert user through UI

If your app displays alerts, play sounds, or badges its icon, you must call this method during your launch cycle to request permission to alert the user in these ways

1
2
3
4
5
let types: UIUserNotificationType = [.Badge, .Sound, .Alert]
let categories = Set<UIUserNotificationCategory>()
let settings = UIUserNotificationSettings(forTypes: types, categories: categories)

UIApplication.sharedApplication().registerUserNotificationSettings(settings)

You don’t need to wait for registerUserNotificationSettings to callback before calling registerForRemoteNotifications

From iOS 10, use UNNotifications framework https://onmyway133.github.io/blog/How-to-register-for-alert-push-notification-in-iOS/

When to register

From Registering, Scheduling, and Handling User Notifications

Never cache a device token; always get the token from the system whenever you need it. If your app previously registered for remote notifications, calling the registerForRemoteNotifications method again does not incur any additional overhead, and iOS returns the existing device token to your app delegate immediately. In addition, iOS calls your delegate method any time the device token changes, not just in response to your app registering or re-registering
The user can change the notification settings for your app at any time using the Settings app. Because settings can change, always call the registerUserNotificationSettings: at launch time and use the application:didRegisterUserNotificationSettings: method to get the response. If the user disallows specific notification types, avoid using those types when configuring local and remote notifications for your app.

didReceiveRemoteNotification

About application:didReceiveRemoteNotification:

Implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible. If your delegate implements both methods, the app object calls the application:didReceiveRemoteNotification:fetchCompletionHandler: method.
If the app is not running when a remote notification arrives, the method launches the app and provides the appropriate information in the launch options dictionary. The app does not call this method to handle that remote notification. Instead, your implementation of the application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method needs to get the remote notification payload data and respond appropriately.

About application:didReceiveRemoteNotification:fetchCompletionHandler:

This is for silent push notification with content-available

Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background
In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a push notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.
If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foreground so that you can update your user interface and display information pertaining to the notification.

How to handle

Usually, the use of push notification is to display a specific article, a specific DetailViewControllerin your app. So the good practices are

  • When the app is in foreground: Gently display some kind of alert view and ask the user whether he would like to go to that specific page or not

  • When user is brought from background to foreground, or from terminated to foreground: Just navigate to that specific page. For example, if you use UINavigationController, you can set that specific page the top most ViewController, if you use UITabBarController, you can set that specific page the selected tab, something like that

1
2
3
4
5
6
7
8
9
10
11
- func handlePushNotification(userInfo: NSDictionary) {
// Check applicationState
if (applicationState == UIApplicationStateActive) {
// Application is running in foreground
showAlertForPushNotification(userInfo)
}
else if (applicationState == UIApplicationStateBackground || applicationState == UIApplicationStateInactive) {
// Application is brought from background or launched after terminated
handlePushNotification(userInfo)
}
}

Here we create another method handlePushNotification to handle push notification. When you receive push notification, 3 cases can occur

Case 1: Foreground

Loud push

  • No system alert

  • application:didReceiveRemoteNotification:fetchCompletionHandler: is called

Silent push

  • No system alert

  • application:didReceiveRemoteNotification:fetchCompletionHandler: is called

Case 2: Background

Loud push

  • System alert

  • No method called

  • Tap notification and application:didReceiveRemoteNotification:fetchCompletionHandler: is called

  • Tap on App Icon and nothing is called

Silent push

  • No system alert

  • application:didReceiveRemoteNotification:fetchCompletionHandler: is called. If app is suspended, its state changed to UIApplicationStateBackground

  • Tap notification and application:didReceiveRemoteNotification:fetchCompletionHandler: is called

  • Tap on App Icon and nothing is called

Case 3: Terminated

Loud push

  • System alert

  • No method called

  • Tap notification and application:didFinishLaunchingWithOptions: with launchOptions, application:didReceiveRemoteNotification:fetchCompletionHandler: is called

  • Tap on App Icon and application:didFinishLaunchingWithOptions: is called with launchOptions set to nil

Silent push

  • No system alert

  • application:didReceiveRemoteNotification:fetchCompletionHandler: is called. If app was not killed by user, it is woke up and state changed to UIApplicationStateInactive.

  • Tap notification and application:didFinishLaunchingWithOptions: with launchOptions, application:didReceiveRemoteNotification:fetchCompletionHandler: is called

  • Tap on App Icon and application:didFinishLaunchingWithOptions: is called with launchOptions set to nil

System alert

System alert only show if the payload contains alert

1
2
3
4
5
6
7
8
9
10
11
12
{
"aps" : {
"alert" : {
"title" : "Game Request",
"body" : "Bob wants to play poker",
"action-loc-key" : "PLAY"
},
"badge" : 5
},
"param1" : "bar",
"param2" : [ "bang", "whiz" ]
}

Silent push payload

For now I see that silent push must contain sound for application:didReceiveRemoteNotification:fetchCompletionHandler: to be called when app is in background

1
2
3
4
5
6
7
8
9
{
“aps”: {
“content-available”: 1,
“alert”: “hello” // include this if we want to show alert
“sound”: “” // this does the trick
},
“param1”: 1,
“param2”: “text”
}

Read Pushing Background Updates to Your App

If your app’s server-based content changes infrequently or at irregular intervals, you can use background notifications to notify your app when new content becomes available. A background notification is a remote notification that doesn’t display an alert, play a sound, or badge your app’s icon. It wakes your app in the background and gives it time to initiate downloads from your server and update its content.

The system treats background notifications as low-priority: you can use them to refresh your app’s content, but the system doesn’t guarantee their delivery. In addition, the system may throttle the delivery of background notifications if the total number becomes excessive. The number of background notifications allowed by the system depends on current conditions, but don’t try to send more than two or three per hour.

How to test Push notification

I built a macOS app called PushNotification for you to test push notification. It works with certificate and the new key authentication with APNS. Please give it a try

Tutorials that use PushNotifications

Troubleshooting

Silent notification with push notification enabled

In theory, if user disables push notification then silent notification still goes through

https://stackoverflow.com/questions/31450403/didreceiveremotenotification-not-working-in-the-background

but sound key should be present

1
2
3
4
5
6
{
aps = {
"content-available" : 1,
sound : ""
};
}

When open the app, didReceiveRemoteNotification is called immediately with the silent push message

Where to go from here

I hope you find this article useful. iOS changes fast so some the things I mention may be outdated by the time you read, if so please let me know. Here are some more interesting links


Original post https://medium.com/fantageek/push-notification-in-ios-46d979e5f7ec

Comments