Skip to content

T-Pham/UILocalNotification-RemotePayload

Repository files navigation

'   _     _  _     ____  ____  ____  _     _      ____  _____  _  _____ _  ____  ____  _____  _  ____  _     
'  / \ /\/ \/ \   /  _ \/   _\/  _ \/ \   / \  /|/  _ \/__ __\/ \/    // \/   _\/  _ \/__ __\/ \/  _ \/ \  /|
'  | | ||| || |   | / \||  /  | / \|| |   | |\ ||| / \|  / \  | ||  __\| ||  /  | / \|  / \  | || / \|| |\ ||
'  | \_/|| || |_/\| \_/||  \_ | |-||| |_/\| | \||| \_/|  | |  | || |   | ||  \_ | |-||  | |  | || \_/|| | \||
'  \____/\_/\____/\____/\____/\_/ \|\____/\_/  \|\____/  \_/  \_/\_/   \_/\____/\_/ \|  \_/  \_/\____/\_/  \|
'                                                                                                            
'   ____  _____ _      ____  _____  _____ ____  ____ ___  _ _     ____  ____  ____                           
'  /  __\/  __// \__/|/  _ \/__ __\/  __//  __\/  _ \\  \/// \   /  _ \/  _ \/  _ \                          
'  |  \/||  \  | |\/||| / \|  / \  |  \  |  \/|| / \| \  / | |   | / \|| / \|| | \|                          
'  |    /|  /_ | |  ||| \_/|  | |  |  /_ |  __/| |-|| / /  | |_/\| \_/|| |-||| |_/|                          
'  \_/\_\\____\\_/  \|\____/  \_/  \____\\_/   \_/ \|/_/   \____/\____/\_/ \|\____/                          
'                                                                                                            

The feature is now officially supported in APNS on iOS 10+ via the apns_collapse_id header.


UILocalNotification-RemotePayload

CI Status GitHub issues Codecov Documentation

GitHub release Platform License

Carthage

CocoaPods CocoaPods downloads

Description

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.

Set-up

  • 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 their id.
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)
}

Usage

Swift:
let localNotification = UILocalNotification(remotePayload: remotePayload)
Objective-C:
UILocalNotification *localNotification = [[UILocalNotification alloc] initWithRemotePayload:remotePayload];

Installation

Add the line below to your Cartfile:

github "T-Pham/UILocalNotification-RemotePayload"

Add the line below to your Podfile:

pod 'UILocalNotification-RemotePayload'

Manually

Add the file /UILocalNotification-RemotePayload/UILocalNotification-RemotePayload.swift to your project. You are all set.

Compatibility

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

License

UILocalNotification-RemotePayload is available under the MIT license. See the LICENSE file for more info.

About

πŸ’Œ Gmail & Facebook Messenger-like notifications

Resources

License

Stars

Watchers

Forks

Packages

No packages published