' _ _ _ ____ ____ ____ _ _ ____ _____ _ _____ _ ____ ____ _____ _ ____ _
' / \ /\/ \/ \ / _ \/ _\/ _ \/ \ / \ /|/ _ \/__ __\/ \/ // \/ _\/ _ \/__ __\/ \/ _ \/ \ /|
' | | ||| || | | / \|| / | / \|| | | |\ ||| / \| / \ | || __\| || / | / \| / \ | || / \|| |\ ||
' | \_/|| || |_/\| \_/|| \_ | |-||| |_/\| | \||| \_/| | | | || | | || \_ | |-|| | | | || \_/|| | \||
' \____/\_/\____/\____/\____/\_/ \|\____/\_/ \|\____/ \_/ \_/\_/ \_/\____/\_/ \| \_/ \_/\____/\_/ \|
'
' ____ _____ _ ____ _____ _____ ____ ____ ___ _ _ ____ ____ ____
' / __\/ __// \__/|/ _ \/__ __\/ __// __\/ _ \\ \/// \ / _ \/ _ \/ _ \
' | \/|| \ | |\/||| / \| / \ | \ | \/|| / \| \ / | | | / \|| / \|| | \|
' | /| /_ | | ||| \_/| | | | /_ | __/| |-|| / / | |_/\| \_/|| |-||| |_/|
' \_/\_\\____\\_/ \|\____/ \_/ \____\\_/ \_/ \|/_/ \____/\____/\_/ \|\____/
'
The extension provides a convenient init method to create UILocalNotification instance from remote payload specified in Apple's Documentation.
It is motivated by the fact that we have more control over local notifications than remote notifications. With local notifications, we can have notifications with behaviours as seen in apps such as Gmail or Facebook Messenger. The Gmail app can clear the notifications of read emails on your iOS device automatically when you have read the emails on a browser. The Messenger app's notification for incoming calls can vibrate and play sound repeatedly without flooding your screen with multiple notifications. Google and Facebook might not use the same technique being discussed here, but we can have notifications with those advanced behaviours for our own apps using the technique explained below.
The idea is, instead of pushing a remote notification and having iOS displayed it to user, we push a silent notification which will invoke our app in the background, then the background code constructs a local notification from the payload and display it to user. Since we hold the local notification instance, we can remove it from the screen whenever we want. The technique only works when Background App Refresh is enabled on user's device and your app have registered background execution. However, if your app is a VoIP app using PushKit
, the technique works even when Background App Refresh is disabled.
- Enable background mode in your app's
Info.plist
file:
Non-VoIP app:
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
VoIP app:
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
</array>
- Register for local notification permission:
UIApplication.sharedApplication().registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Badge, .Sound, .Alert], categories: nil))
- Register for remote notification:
Non-VoIP app:
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
if notificationSettings.types != .None {
application.registerForRemoteNotifications()
}
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// pass the deviceToken to the server
}
VoIP app:
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
if notificationSettings.types != .None {
let pushRegistry = PKPushRegistry(queue: dispatch_get_main_queue())
pushRegistry.delegate = self
pushRegistry.desiredPushTypes = [PKPushTypeVoIP]
}
}
extension AppDelegate: PKPushRegistryDelegate {
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
// pass credentials.token to the server
}
}
- Push a notification from the server. You may want to assign it an
id
, so you can add the logic to update notifications based on theirid
.
Non-VoIP app:
Push a silent remote notification. Put the payload to an object other than the top-level aps
so that the notification remains silent. Here we put it in the payload
object.
{
"aps" : {
"content-available": 1
},
"payload" : {
"aps" : {
"alert" : "hello world"
},
"id" : 1
}
}
VoIP app:
Just push a normal payload because VoIP notifications are always silent.
{
"aps" : {
"alert" : "hello world"
},
"id" : 1
}
- Handle the remote notification:
Non-VoIP app:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
let localNotification = UILocalNotification(remotePayload: userInfo["payload"] as? [NSObject: AnyObject] ?? [:])
if let id = userInfo["payload"]?["id"] as? Int {
if let previousLocalNotification = self.localNotificationsMap[id] {
UIApplication.sharedApplication().cancelLocalNotification(previousLocalNotification)
}
self.localNotificationsMap[id] = localNotification
}
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
completionHandler(.NoData)
}
VoIP app:
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
let localNotification = UILocalNotification(remotePayload: payload.dictionaryPayload)
if let id = payload.dictionaryPayload["id"] as? Int {
if let previousLocalNotification = self.localNotificationsMap[id] {
UIApplication.sharedApplication().cancelLocalNotification(previousLocalNotification)
}
self.localNotificationsMap[id] = localNotification
}
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
Swift:
let localNotification = UILocalNotification(remotePayload: remotePayload)
Objective-C:
UILocalNotification *localNotification = [[UILocalNotification alloc] initWithRemotePayload:remotePayload];
Add the line below to your Cartfile:
github "T-Pham/UILocalNotification-RemotePayload"
Add the line below to your Podfile:
pod 'UILocalNotification-RemotePayload'
Add the file /UILocalNotification-RemotePayload/UILocalNotification-RemotePayload.swift
to your project. You are all set.
From version 2.0.0, Swift 3 syntax is used. If your project is still using Swift version 2, please use a UILocalNotification-RemotePayload version prior to 2.0.0.
Podfile
pod 'UILocalNotification-RemotePayload', '~> 1.0.4'
or Cartfile
github "T-Pham/UILocalNotification-RemotePayload" ~> 1.0.4
UILocalNotification-RemotePayload is available under the MIT license. See the LICENSE file for more info.