Send mobile push notifications
One of the features that PubNub offers is the ability to connect to Android and iOS push notification services.
This is done by creating a key from one, or both, of those providers and adding it to your PubNub Admin Portal.
It's possible to use Google/Firebase in iOS applications, so the choice is yours whether you want to manage both services.
Request a push notification token for the device
For every device that you'd like to receive a push notification on, you must register with the notification provider. Refer to the Android, iOS, and Firebase for iOS documentation on how to get this set up.
Register and cache your token
Now that you're set up with your token, you'll need to make sure as tokens are created by your service, that your app is always using the correct token. This means collecting, replacing, and caching. For caching you can use UserDefaults
for iOS and SharedPreferences
for Android.
To create the cache:
- Swift
- Objective-C
- Java
- Kotlin
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")
}
}
}
static dispatch_queue_t deviceTokenDispatch;
static dispatch_queue_t pushChannelsDispatch;
static dispatch_once_t registerCustomDispatchOnceToken;
- (void) registerCustomDispatchQueues {
dispatch_once(®isterCustomDispatchOnceToken, ^{
deviceTokenDispatch = dispatch_queue_create("com.pubnub.deviceToken", DISPATCH_QUEUE_CONCURRENT);
pushChannelsDispatch = dispatch_queue_create("com.pubnub.pushChannels", DISPATCH_QUEUE_CONCURRENT);
});
}
- (nullable NSData *) cachedToken {
__block NSData* token;
dispatch_sync(tokenDispatch, ^{
token = [[NSUserDefaults standardUserDefaults] dataForKey: @"com.pubnub.deviceToken"];
show all 23 linespublic class SharedPreferencesManager {
private static SharedPreferences sharedPref;
private SharedPreferencesManager() { }
public static void init(Context context) {
if(sharedPref == null)
sharedPref = context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
}
public static final String FCM_DEVICE_TOKEN = "PUBNUB_FCM_DEVICE_TOKEN";
public static @Nullable String readDeviceToken() {
return sharedPref.getString(FCM_DEVICE_TOKEN, null)
}
public static void writeDeviceToken(String value) {
SharedPreferences.Editor prefsEditor = sharedPref.edit();
show all 29 linesclass SharedPreferencesManager private constructor() {
companion object {
private val sharePref = SharedPreferencesManager()
private lateinit var sharedPreferences: SharedPreferences
private val PLACE_OBJ = "place_obj"
private val FCM_DEVICE_TOKEN = "PUBNUB_FCM_DEVICE_TOKEN"
private val FCM_CHANNEL_LIST = "PUBNUB_FCM_CHANNEL_LIST"
fun getInstance(context: Context): SharedPreferencesManager {
if (!::sharedPreferences.isInitialized) {
synchronized(SharedPreferencesManager::class.java) {
if (!::sharedPreferences.isInitialized) {
sharedPreferences = context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
show all 28 linesNow, let's catch and replace the tokens as they come in.
- Swift
- Objective-C
- Java
- Kotlin
For more information regarding registering 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)")
}
}
}
For more information regarding registering token, go to Apple Docs.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSData *oldDevice = self.cachedToken;
if ([deviceToken isEqualToData:oldDevice]) { return; }
self.cachedToken = deviceToken;
[self updateAPNSDevicesOnChannels:[self.pushChannels allObjects]
withDevicePushToken:deviceToken
replacingDevicePushToken:oldDevice
pushType:PNAPNS2Push
environment:PNAPNSProduction
topic:@"com.mycompany.mybundleid"
andCompletion:^(PNAcknowledgmentStatus * _Nonnull status) {
if (!status.isError) {
NSLog(@"Successfully updated channels with new token");
} else {
show all 19 linespublic class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override public void onNewToken(String token) {
String oldToken = SharedPreferencesManager.readDeviceToken();
if (token.equals(oldToken)) { return; }
SharedPreferencesManager.write(token);
updatePushNotificationsOnChannels(SharedPreferencesManager.readChannelList(), token, oldToken);
}
}
override fun onNewToken(token: String) {
val oldToken = SharedPreferencesManager.getInstance(applicationContext).readDeviceToken()
if (token == oldToken) { return }
SharedPreferencesManager.getInstance(applicationContext).writeDeviceToken(token)
updatePushNotificationsOnChannels(
SharedPreferencesManager.getInstance(applicationContext).readChannelList(),
token,
oldToken
)
}
Connect the device to a channel
For your app to know that what you're sending is going to be a push notification, you'll need to define certain channels to be push channels. In the code examples below you'll see Ch1
and Ch2
as the two channels being added.
- Swift
- Objective-C
- Java
- Kotlin
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)")
}
}
}
NSData *deviceToken = [(AppDelegate *)[[UIApplication sharedApplication] delegate] cachedToken];
if (deviceToken == nil) { return; }
[self.client addPushNotificationsOnChannels:@[@"ch1",@"ch2"]
withDevicePushToken:deviceToken
pushType:PNAPNS2Push
environment:PNAPNSProduction
topic:@"com.mycompany.mybundleid"
andCompletion:^(PNAcknowledgmentStatus *status) {
if (!status.isError) {
NSSet* cachedChannels = [(AppDelegate *)[[UIApplication sharedApplication] delegate] pushChannels];
[(AppDelegate *)[[UIApplication sharedApplication] delegate] setPushChannels:[cachedChannels setByAddingObjectsFromArray:@[@"ch1",@"ch2"]]];
} else { NSLog(@"Failed to add Push due to: %@", status); }
}];
String cachedToken = SharedPreferencesManager.readDeviceToken();
pubnub.addPushNotificationsOnChannels()
.pushType(PNPushType.FCM)
.deviceId(cachedToken)
.channels(Arrays.asList("ch1", "ch2", "ch3"))
.async(result -> { /* check result */ });
SharedPreferencesManager.getInstance(applicationContext).readDeviceToken()?.also { deviceToken ->
pubnub.addPushNotificationsOnChannels(
pushType = PNPushType.FCM,
deviceId = deviceToken,
channels = listOf("ch1", "ch2", "ch3")
).async { result, status ->
// Handle Response
}
}
Building the push notification message
When constructing the Push message you wish to send you need to specify whether you're using Apple Push Notification pn_apns
or Firebase (Google) Cloud Message pn_fcm
in your payload. APN will require you to define the environment (development
or production
) and the Bundle ID of your app using topic
.
For Apple Push Notification Service:
- JSON
- Swift
- Objective-C
{
"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 lineslet message = ["text": "John invited you to chat"]
let pushPayload = PubNubPushMessage(
apns: PubNubAPNSPayload(
aps: APSPayload(alert: .object(.init(title: "Chat Invitation", body: "John invited you to chat"))),
pubnub: [.init(targets: [.init(topic: "com.mycompany.mybundleid", environment: .production)])],
),
additional: message
)
NSDictionary *message = @{
@"text": @"John invited you to chat"
};
PNNotificationsPayload *pushData = [PNNotificationsPayload
payloadsWithNotificationTitle:@"Chat Invitation"
body:@"John invited you to chat"];
// create the APNs target with topic and environment
PNAPNSNotificationTarget *target = [PNAPNSNotificationTarget
targetForTopic:@"com.mycompany.mybundleid"
inEnvironment:PNAPNSProduction
withExcludedDevices:nil];
// create the APNs config object and add the target
show all 23 linesFor Firebase:
- Swift
- Objective-C
- JSON(Firebase)
- Java
- Kotlin
let message = ["text": "John invited you to chat"]
let pushPayload = PubNubPushMessage(
fcm: PubNubFCMPayload(
payload: nil,
target: .topic("invitations"),
apns: FCMApnsConfig(
headers: [
"apns-push-type": "alert", "apns-topic": "com.mycompany.mybundleid", "apns-priority": "10"
],
payload: APSPayload(alert: .object(.init(title: "Chat Invitation", body: "John invited you to chat")))
)
),
additional: message
)
NSDictionary *message = @{
@"text": @"John invited you to chat"
};
NSDictionary *pushPayload = @{
@"text": @"John invited you to chat",
@"pn_fcm": @{
@"notification": @{
@"title": @"Chat Invitation",
@"body": @"John invited you to chat"
},
@"apns": @{
@"payload": @{
@"aps": @{
@"alert": @{
show all 29 lines{
"text": "John invited you to chat",
"pn_fcm": {
"android": {
"notification": {
"title": "Chat Invitation",
"body": "John invited you to chat"
}
},
"pn_exceptions" : ["device-token1", "device-token2"]
}
}
PushPayloadHelper pushPayloadHelper = new PushPayloadHelper();
PushPayloadHelper.FCMPayload fcmPayload = new PushPayloadHelper.FCMPayload();
PushPayloadHelper.FCMPayload.Notification fcmNotification =
new PushPayloadHelper.FCMPayload.Notification()
.setTitle("Chat Invitation")
.setBody("John invited you to chat");
fcmPayload.setNotification(fcmNotification);
pushPayloadHelper.setFcmPayload(fcmPayload);
Map<String, Object> commonPayload = new HashMap<>();
commonPayload.put("text", "John invited you to chat");
pushPayloadHelper.setCommonPayload(commonPayload);
Map<String, Object> pushPayload = pushPayloadHelper.build();
val pushPayloadHelper = PushPayloadHelper()
val fcmPayload = FCMPayload().apply {
notification = FCMPayload.Notification().apply {
title = "Chat Invitation"
body = "John invited you to chat"
}
custom = mapOf(
"topic" to "invitations",
"pn_exceptions" to arrayOf("device-token1", "device-token2")
)
}
pushPayloadHelper.fcmPayload = fcmPayload
pushPayloadHelper.commonPayload = mapOf(
show all 19 linesSend the Push Notification
Just like when sending any other message with PubNub, we use the publish method to mobile push notifications. For mobile push notifications to work we must send a message on the channels that we registered for mobile push notifications in the previous steps.
- Java
- Kotlin
- JavaScript
- Swift
- Objective-C
pubnub.publish()
.channel("ch1")
.message(pushPayload)
.async(result -> { /* check result */ });
pubnub.publish(
channel = "ch1",
message = pushPayload
).async { result ->
// Handle Response
}
//publish on channel
pubnub.publish(
{
channel: "ch1"
message: pushPayload
},
function (status, response) {
// Handle Response
}
);
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)")
}
}
[self.pubnub publish:message toChannel:@"ch1" mobilePushPayload:pushPayload
withCompletion:^(PNPublishStatus *status) {
if (!status.isError) {
NSLog(@"Successfully published message");
} else {
NSLog(@"Failed to publish due to: %@", status);
}
}];