iOS Push Notifications

HTTP/2-based APNs

This APNs tutorial uses the HTTP/2-based Apple Push Notification service (APNs), not the legacy APNs binary interface, to send notifications.

PubNub Push Notifications gateway bridges native message publishing with third-party push notification services including Apple Push Notification service (APNs) and Firebase Cloud Messaging (FCM). The following tutorial will show a sample implementation of the iOS app delegate methods needed to register for remote notifications, receive the corresponding token and add the device token to a PubNub channel.

iOS Push Notification How-to and Tutorial

For more information on Push Notifications within PubNub, you can view our in-depth How-To. You can also check the iOS Push Notification How-To with follow along video and the iOS Push Notificatons Tutorial.

Step 1: Configure Account Settings

Follow the steps of this APNs tutorial to configure APNs Push Notifications on your app.

Step 1a: Create an APNs Authentication Token

Log in to your Apple Developer account.

Apple Developer account

Go to Certificates, IDs & Profiles, and select Keys.

Certificates, IDs & Profiles

Create and obtain a valid APNs Authentication Token (.p8 file extension).

Register a New Key

After downloading, the Auth Key filename will look like this AuthKeyABCD1234.p8, the ABCD1234 is the Key ID for this key, we will need this Key ID later. You can also find the Auth Key ID by selecting your push token from your list of available Apple Developer Account Keys.

View Key Details

Step 1b: Upload the APNs Authentication Token to Your Admin Portal

On the Admin Portal, go to the Mobile Push Notifications section from the Keys page. Click Upload Token File to upload your APNs Authentication Token. The Team ID and Auth Key ID fields will be populated automatically. See how to enable Push Notifications with APNs.

Upload token

Step 2: Requesting Device Token

To add Notification support to your application, start by enabling the Push Notifications capability in your Xcode project.

Upload token

For this example, all the code will be put inside the AppDelegate, but any globally accessible object can be used to provide the additional non-system functionality.

Import PubNub into your project:

import UserNotifications
import PubNub

When your application is launched, or whenever appropriate for your use cases, you can use UNUserNotificationCenter to request notification permissions from your user.

Inside the completion block, you can request a Device Token specific to your application.

For more information regarding requesting tokens, go to Apple Docs.

func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {

UNUserNotificationCenter.current().delegate = self

UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, err) in
DispatchQueue.main.async() {
UIApplication.shared.registerForRemoteNotifications()
}
}

return true
}

Refer to the Apple Developer documentation for more information on how to register your app and obtain an APNs Authentication Token.

Step 3: Receiving & Monitoring Device Token

Calling registerForRemoteNotfiications() will initiate the registration process with Apple Push Notification service (APNs).

The system will provide a token via the UIApplicationDelegate by calling the following delegate method. After the initial APNs registration and device token has been delivered, new (sometimes duplicate) tokens will be delivered to this delegate method and your PubNub channel registrations will need to be updated.

For more information on receiving token, go to Apple Docs.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let oldDeviceToken = self.cachedToken
guard deviceToken != oldDeviceToken else { return }
self.cachedToken = deviceToken

updateAPNSDevicesOnChannels(
pushChannels.allObjects, newDevice: deviceToken, oldDevice: oldDeviceToken,
on: "com.mycompany.mybundleid", environment: .production
) { result in
switch result {
case .success: print("Successfully updated channels with new token")
case let .failure(error): print("Failed to update device token due to: \(error)")
}
}
}

Step 3a: Caching Device Token & Registered Channels

To ensure that an application can properly handle adding, updating, and removing registered push channels there are two pieces of information that should be cached to avoid race conditions: the Device Token and the list of registered channels. Not only will properly caching allow for easy access from anywhere inside the application, but it will also prevent race conditions when multiple registration operations are queued at the same time.

UserDefaults provides basic persistent storage, but can be replaced by more sophisticated storage as your use-case requires. The following code will ensure the accessing of the cached information will be thread-safe regardless of storage choice.

A new Device Token can be provided from the system at any time, and should be stored whenever it's received.

let tokenDispatch = DispatchQueue(label: "com.pubnub.deviceToken", attributes: .concurrent)
var cachedToken: Data? {
get {
var token: Data?
tokenDispatch.sync {
token = UserDefaults.standard.data(forKey: "com.pubnub.deviceToken")
}
return token
}
set {
tokenDispatch.async(flags: .barrier) {
UserDefaults.standard.set(newValue, forKey: "com.pubnub.deviceToken")
}
}
}

The list of registered channels should also be cached in a similar manner as the Device Token and should be updated whenever registered channels are added or removed. Set is used for the convenience of ensuring there are no duplicate channels.

let pushChannelDispatch = DispatchQueue(label: "com.pubnub.pushChannels", attributes: .concurrent)
var pushChannels: Set<String> {
get {
var channels: Set<String>?
pushChannelDispatch.sync {
channels = UserDefaults.standard.object(forKey: "com.pubnub.pushChannels") as? Set<String>
}
return channels ?? []
}
set {
pushChannelDispatch.async(flags: .barrier) {
UserDefaults.standard.set(newValue, forKey: "com.pubnub.pushChannels")
}
}
}

Step 3b: Updating Existing Registrations

A simple helper method can be created to consolidate the remove-then-add functionality when updating your existing registered channels.

func updateAPNSDevicesOnChannels(_ channels: [String], newDevice: Data, oldDevice: Data?,
on topic: String, environment: PubNub.PushEnvironment, completion: @escaping ((Result<[String], Error>) -> Void)
) {
if let oldDevice = oldDevice {
pubnub.removeAllAPNSPushDevice(for: oldDevice, on: topic, environment: environment) { result in
switch result {
case .success: print("Successfully removed device token")
case let .failure(error): print("Failed to remove device token due to: \(error)")
}
}
}
pubnub.addAPNSDevicesOnChannels(
channels, device: newDevice, on: topic, environment: environment, completion: completion
)
}

Step 4: Managing Device Registrations

Once a Device Token is obtained, it can be registered with a list of channels to allow Push Notifications to be sent to the device. Channels can be dynamically added and removed based on the use cases of the application, and the current registrations for a Device Token can also be viewed.

Step 4a: Register New Channels

When adding channels it's recommended to obtain the Device Token and List of Registered Channels from a cached source. After successfully registering channels, the newly registered channels should be added to the cached list.

if let deviceToken = (UIApplication.shared.delegate as? AppDelegate)?.cachedToken {
pubnub.addAPNSDevicesOnChannels(
["ch1", "ch2"],
device: deviceToken,
on: "com.mycompany.mybundleid",
environment: .production
) { result in
switch result {
case let .success(channelsAdded):
channelsAdded.forEach { (UIApplication.shared.delegate as? AppDelegate)?.pushChannels.update(with: $0) }
case let .failure(error): print("Failed to add Push due to: \(error.localizedDescription)")
}
}
}

Step 4b: List Registered Channels

Once device registrations are added, you can confirm the APNs registrations for the device by listing all channels that the device is registered with. Since the list on the server is the source-of-truth, we will update our cached list to reflect the channels currently registered on the server.

guard let deviceToken = (UIApplication.shared.delegate as? AppDelegate)?.cachedToken else { return }

pubnub.listAPNSPushChannelRegistrations(
for: deviceToken,
on: "com.mycompany.mybundleid",
environment: .production
) { result in
switch result {
case let .success(channelsRegistered):
(UIApplication.shared.delegate as? AppDelegate)?.pushChannels = Set(channelsRegistered)
case let .failure(error): print("Failed to add Push due to: \(error.localizedDescription)")
}
}

Step 4c: Remove Existing Registrations

When removing channels, it's recommended to obtain the Device Token and List of Registered Channels from a cached source. After removing registering channels, the channels that were removed should be also removed from the cached source.

if let deviceToken = (UIApplication.shared.delegate as? AppDelegate)?.cachedToken {
pubnub.removeAPNSDevicesOnChannels(
["ch1", "ch2"],
device: deviceToken,
on: "com.mycompany.mybundleid",
environment: .production
) { result in
switch result {
case let .success(channelsRemoved):
channelsRemoved.forEach { (UIApplication.shared.delegate as? AppDelegate)?.pushChannels.remove($0) }
case let .failure(error): print("Failed to add Push due to: \(error.localizedDescription)")
}
}
}

Step 5: Construct the Push Payload

On iOS, there are two supported push providers that PubNub will use to relay Push Notifications to the application: APNs and APNs via Firebase. To send a push notification, include the appropriate push notification payload for APNs, FCM, or both when you publish a message and PubNub will appropriately parse the message.

Step 5a: Constructing APNs Payloads

The pn_apns payload is required for all APNs notifications. It tells PubNub that the payload should be forwarded to Apple.

pn_apns consists of:

{
"pn_apns": {
"aps": {
// necessary items
},
"pn_push": {
// necessary items
},
"pn_debug": true,
"pn_ttl": 60
}

aps

The aps payload is entirely managed by Apple. It's a dictionary that contains the keys used by Apple to deliver the notification to the user's device. The keys specify the type of interactions that you want the system to use when alerting the user. Depending on your use case, providing content for this payload may be required. For example, if you decide to instruct the system to handle the notification in the background, you must add the content-available key, set it to 1, and not include any visual or audio keys. Refer to the Payload Key Reference section in the Apple Developer documentation for this and other examples.

pn_push

The pn_push object contains the configuration of the push message. PubNub uses this payload to set such values as request headers in APNs notifications sent to Apple. This payload is required for all APNs notifications.

KeyTypeRequiredDefaultDescription
push_typeStringNoalertA type of notification sent to Apple. Available values: alert, background, voip, complication, fileprovider, or mdm. The value you set for this key should always align with the payload values you set inside your aps payload. Refer to the apns_push_type header field in the Apple Developer documentation for more information.
auth_methodStringNotokenAvailable values: token, cert, or certificate
targets:environmentStringNodevelopmentAn Apple environment for the certificate associated with the PubNub key-set in use. Available values: development or production.
targets:topicStringYesA Bundle ID of your target application
targets:excluded_devices[String]NoA list of device tokens that should be excluded from receiving the notification. Typically, you can add your own device so you don't get a notification you publish.
versionStringYesSet to v2.
pn_collapse_idStringNoAn identifier to join multiple notifications into a single notification.

Additional Parameters

KeyTypeRequiredDescription
pn_debugbooleanNoA flag that enables publishing push debugging info to the pndebug channel. For more information, refer to Mobile Push Troubleshooting.
pn_ttlintNoTime in seconds after which the notification expires.

In the code sample below, the message includes a pn_apns payload to trigger APNs notifications on iOS devices:

For more information on generating notification, go to Apple Docs.

{
"text": "John invited you to chat",
"pn_apns": {
"aps": {
"alert": {
"title": "Chat Invitation",
"body": "John invited you to chat"
}
},
"pn_push":[
{
"push_type": "alert",
"auth_method": "token",
"targets":[
{
show all 25 lines

Step 5b: Constructing APNs via Firebase Payloads

Firebase Configuration

The prior steps in this tutorial have focused exclusively on configuration the application to use APNs directly. Please see PubNub's tutorial on how to configure your application to use Firebase.

The structure of the pn_gcm payload specific to APNs notifications is as follows:

FieldTypeRequiredDescription
apns:payload:apsJSONYesThe aps dictionary contains the keys used by Apple to deliver the notification to the user's device. The keys specify the type of interactions that you want the system to use when alerting the user. Refer to the Apple Developer documentation for more information.
apns:headersStringNoHTTP request headers defined in Apple Push Notification Service, for example "apns-push-type": "alert"
pn_exceptions[String]NoA list of Device Tokens that should be excluded from receiving the notification.

One of the following fields are required to identify the target of the notification:

FieldTypeDescription
tokenStringRegistration token to send a message to
topicStringTopic name to send a message to, for example "weather". Note: "/topics/" prefix should not be provided.
conditionStringCondition to send a message to, for example "'foo' in topics && 'bar' in topics"

For more information on REST resources, go to Firebase Docs.

{
"text": "John invited you to chat",
"pn_gcm": {
"topic": "invitations",
"apns": {
"payload": {
"aps": {
"alert": {
"title": "Chat Invitation",
"body": "John invited you to chat"
}
}
},
"headers": {
"apns-push-type": "alert",
show all 22 lines

Step 6: Publishing the Push Notification

Once the push payload is ready, use the publish method to publish the message on a channel. When PubNub finds the pn_apns and/or pn_gcm payloads, it will retrieve all device tokens that are associated with the push payload on the target channel and forward a push notification request to the appropriate push service for those associated devices.

pubnub.publish(channel: "ch1", message: pushPayload) { result in
switch result {
case let .success(timetoken):
print("Successfully published message at: \(timetoken)")
case let .failure(error):
print("Failed to publish due to: \(error)")
}
}

For more information about push troubleshooting, refer to Mobile Push Troubleshooting.

Last updated on