iOS Mobile 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 Mobile Push Notifications bridges native message publishing with third-party push services, including Apple Push Notification service (APNs) and Firebase Cloud Messaging (FCM). This tutorial shows how to register for remote notifications, receive the device token, and add the device token to a PubNub channel.

Push notifications are channel‑specific. A user receives pushes for channels where push is enabled. In‑app messages still require a channel subscription.

Step 1: Configure account settings

Follow these steps to configure APNs Mobile Push Notifications for 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: Configure mobile push notifications in the Admin Portal

In the Admin Portal, open your app’s keyset.

Upload token

Enable Mobile Push Notifications and provide values for the Team ID and Auth Key ID (Apple's key identifier) fields.

Then, upload your APNs Authentication Token through the Token File option.

Step 2: Request device token

Enable the Mobile Push Notifications capability in your Xcode project.

Upload token

For this example, place the code in the AppDelegate. Any globally accessible object works for non‑system logic.

Import PubNub into your project:

import UserNotifications
import PubNubSDK

When the app launches, or any time it makes sense, use UNUserNotificationCenter to request notification permissions.

Inside the completion block, request the device token for your app.

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: Receive & monitor device token

Calling registerForRemoteNotifications() initiates registration with Apple Push Notification service (APNs).

The system provides a token via UIApplicationDelegate by calling the following method. After initial registration, APNs may deliver new (sometimes duplicate) tokens. Update your PubNub channel registrations when the token changes.

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: Cache 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; you can replace it with more advanced storage as needed. The following code makes access to cached information thread‑safe.

The system can provide a new device token at any time. Store the token whenever you receive it.

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")
}
}
}

Cache the list of registered channels the same way as the device token. Update it whenever you add or remove channels. Using a Set helps prevent duplicates.

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: Update 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: Manage device registrations

Once a Device Token is obtained, it can be registered with a list of channels to allow Mobile 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 you add channels, read the device token and registered channels from the cache. After registration, add the channels 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

After you add registrations, confirm APNs registrations by listing all channels for the device. The server list is the source of truth. Update the cached list to match 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 you remove channels, read the device token and registered channels from the cache. After removal, also remove those channels from the cache.

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, APNs is the push provider PubNub uses to relay Mobile Push Notifications to your app. To send a push, include the APNs payload when you publish. PubNub parses the message and forwards it to Apple.

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_type
String
No
alert
A 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_method
String
No
token
Available values: token, cert, or certificate
targets:environment
String
No
development
An Apple environment for the certificate associated with the PubNub key-set in use. Available values: development or production.
targets:topic
String
Yes
A Bundle ID of your target application
targets:excluded_devices
[String]
No
A 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.
version
String
Yes
Set to v2.
pn_collapse_id
String
No
An identifier to join multiple notifications into a single notification.

Additional parameters

KeyTypeRequiredDescription
pn_debug
boolean
No
A flag that enables publishing push debugging info to the pndebug channel. For more information, refer to Mobile Push Troubleshooting.
pn_ttl
int
No
A PubNub push property. It represents the number of seconds the notification is valid for from the time the notification is published to PubNub until the push server processes the publish.

This flag only determines if PubNub forwards the notification to APNS, there is no consideration of a user device state.

The default value for this parameter is 3600 seconds (1 hour). You don't have to set it if you provide expirationDate (used in an APNS POST request as the apns-expiration header value) in the push notification configuration.

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

Metadata in message payloads

You can provide custom information to the app when it receives the notification by adding metadata to the message payload. For Apple Push Notification Service (APNs), include metadata as key-value pairs outside the aps dictionary in custom fields.

{
"aps": {
"alert": {
"title": "New Message",
"body": "You have received a new message."
}
},
"customData": {
"chatId": "78910",
"notificationType": "messageReceived"
}
}

Prevent self-notifications

Exclude the sender’s device token so they don’t receive a push for their own message. Add the token(s) to targets.excluded_devices in the pn_push entry:

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

Step 6: Publish 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_fcm 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