Service Extensions
Using the iOS and Android Notification Service Extension in your mobile apps.
Quick Reference | Details |
---|---|
iOS Notification Service Extension | Follow the SDK Setup Guide used in your app to get started if not done so already. - See Getting a Payload from a notification within the NSE. You must display content within the push for iOS to handle the payload within the NSE while the App is not in-focus. |
Troubleshooting iOS Notification Service Extension | If you run into issues with the iOS NSE not working, like not getting images or badges not handled correctly. |
Android Notification Service Extension | Setup the optional NotificationServiceExtension class in your app to receive data in the background with or without displaying a notification.Override specific notification settings depending on client-side app logic such as custom accent color, vibration pattern, or other any other NotificationCompat options available. See Android's documentation on the NotificationCompat options. |
iOS Notification Service Extension
Setup instructions can be found in each individual SDK setup guide.
Apple's docs on the UNNotificationServiceExtension.
The following features requires the Notification Service Extension:
- Confirmed Deliveries
- Badges
- Media Attachments (images, video, & audio clip)
- Action Buttons
- Influenced Opens with Firebase Analytics
Getting a payload from an iOS notification
In this example, we are setting Additional Data through the OneSignal dashboard to {"custom_message":"Hullabaloo, Caneck, Caneck"}
NSDictionary* dict = request.content.userInfo;
NSString* custom = [dict objectForKey:@"custom"];
NSString* aps = [dict objectForKey:@"aps"];
NSLog(@"Running NotificationServiceExtension: custom = %@", custom);
NSLog(@"Running NotificationServiceExtension: aps = %@", aps);
let userInfo = request.content.userInfo
print("Running NotificationServiceExtension: userInfo = \(userInfo.description)")
Example console output:
The additional data can then be extracted from the custom
object using the a
parameter...


Troubleshooting the iOS Notification Service Extension
If you are having issues with Confirmed Deliveries or Images not showing on iOS mobile apps follow this guide.
1 Open the NotificationService.m
or NotificationService.swift
and replace the whole file contents with the below code. (The code is the same as our original setup code, just adding some additional logging.
import UserNotifications
import OneSignal
import os.log
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var receivedRequest: UNNotificationRequest!
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.receivedRequest = request;
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
let userInfo = request.content.userInfo
let custom = userInfo["custom"]
print("Running NotificationServiceExtension: userInfo = \(userInfo.description)")
print("Running NotificationServiceExtension: custom = \(custom.debugDescription)")
//debug log types need to be enabled in Console > Action > Include Debug Messages
os_log("%{public}@", log: OSLog(subsystem: "com.your.bundleid", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription)
if let bestAttemptContent = bestAttemptContent {
OneSignal.didReceiveNotificationExtensionRequest(self.receivedRequest, with: self.bestAttemptContent)
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
OneSignal.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
contentHandler(bestAttemptContent)
}
}
}
#import <OneSignal/OneSignal.h>
#import "NotificationService.h"
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNNotificationRequest *receivedRequest;
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.receivedRequest = request;
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
[OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest withMutableNotificationContent:self.bestAttemptContent];
// DEBUGGING: Uncomment the 2 lines below and comment out the one above to ensure this extension is excuting
// Note, this extension only runs when mutable-content is set
// Setting an attachment or action buttons automatically adds this
NSLog(@"Running NotificationServiceExtension");
self.bestAttemptContent.body = [@"[Modified] " stringByAppendingString:self.bestAttemptContent.body];
NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.onesignal.jonexample.onesignal"];
NSLog(@"NSE player_id: %@", [userDefault stringForKey:@"GT_PLAYER_ID"]);
NSLog(@"NSE app_id: %@", [userDefault stringForKey:@"GT_APP_ID"]);
self.contentHandler(self.bestAttemptContent);
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
[OneSignal serviceExtensionTimeWillExpireRequest:self.receivedRequest withMutableNotificationContent:self.bestAttemptContent];
self.contentHandler(self.bestAttemptContent);
}
@end
2 Set your Active Scheme to the OneSignalNotificationServiceExtension
.

3 Build and run the project in Xcode on your device. Then select Window > Devices and Simulators

4 Select Open Console

5 In the Console
- Select Action > Include Debug Messages
- Search for
OneSignalNotificationServiceExtension
as the CATEGORY - Send this device a notification with a message (
contents
)
You should see a message logged with the app running and not running.


Android Notification Service Extension
Add the below Notification Extension Code if you want to do one of the following:
- Receive data in the background with or without displaying a notification.
- Override specific notification settings depending on client side app logic such as custom accent color, vibration pattern, or other any other
NotificationCompat
options available. See Android's documentation on the NotificationCompat options.
Requires writing Native Android code
If using OneSignal SDK Versions:
- Android 4.0.0+
- React Native 4.0.0+
- Flutter 3.0.0+
- Cordova/Ionic 3.0.0+
Go to updated Service Extensions Guide.
See Mobile SDKs API Migration Guides to upgrade the OneSignal SDK.
Follow the below steps if using Unity, Xamarin, Huawei or have not updated to the SDK versions listed above.
Note for Huawei SDK: For the default "message type" notifications Huawei doesn't provide a hook for the app to get this event. However if you send the message as a "data type" this will work. The downside is Huawei devices force kill apps often so "data type" messages may not be as reliable as the default "message type". This is because the "message type" is displayed directly by HMS Core on the device and doesn't wake / start the app.
Step 1. Create a class for the Service Extension
Create a class that extends NotificationExtenderService
and implement the onNotificationProcessing
method.**
import android.support.v4.app.NotificationCompat;
import com.onesignal.OSNotificationDisplayedResult;
import com.onesignal.OSNotificationPayload;
import com.onesignal.NotificationExtenderService;
import com.onesignal.OSNotificationReceivedResult;
import java.math.BigInteger;
import android.util.Log;
public class NotificationServiceExtensionExample extends NotificationExtenderService {
@Override
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
OverrideSettings overrideSettings = new OverrideSettings();
overrideSettings.extender = new NotificationCompat.Extender() {
@Override
public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
//Force remove push from Notification Center after 30 seconds
builder.setTimeoutAfter(30000);
// Sets the icon accent color notification color to Green on Android 5.0+ devices.
builder.setColor(new BigInteger("FF00FF00", 16).intValue());
builder.setContentTitle("New Message");
builder.setContentText("New Encrypted Message");
return builder;
}
};
OSNotificationDisplayedResult displayedResult = displayNotification(overrideSettings);
Log.d("OneSignalExample", "Notification displayed with id: " + displayedResult.androidNotificationId);
// Return true to stop the notification from displaying.
return false;
}
}
Step 2. Add the following to your AndroidManifest.xml
.**
AndroidManifest.xml
.**Replace 'YOUR_CLASS_NAME' with the class name you used above.
<service
android:name=".YOUR_CLASS_NAME"//.NotificationExtenderExample in example below
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="com.onesignal.NotificationExtender" />
</intent-filter>
</service>
Getting a payload from a notification
The OSNotificationReceivedResult
class has a parameter payload
of type OSNotificationPayload which can be used to get the notification message, additional data, and more.
Additionally, the payload
object has the following two parameters:
Name | Type | Details |
---|---|---|
restoring | boolean | True if the notification was restored after an app update, device reboot, and app opened after being force killed. If you have customized any alerting / priority settings check the restoring flag before applying them. You may want to omit displaying if your notification is no longer relevant to the user. |
isAppInFocus | boolean | True if the app is open and in focus when the notification arrives. |
EXAMPLE:
public class NotificationServiceExtensionExample extends NotificationServiceExtension {
@Override
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
OverrideSettings overrideSettings = new OverrideSettings();
overrideSettings.extender = new NotificationCompat.Extender() {
@Override
public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
if (!receivedResult.restoring) {
// Only set custom channel if notification is not being restored
// Note: this would override any channels set through the OneSignal dashboard
return builder.setChannelId("News");
}
}
};
/* Do something with notification payload */
String title = receivedResult.payload.title;
String body = receivedResult.payload.body;
String additionalData = receivedResult.payload.additionalData;
// Writes additionalData values to local database
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ARTICLE_TITLE, additionalData.articleTitle);
values.put(FeedEntry.COLUMN_NAME_ARTICLE_ID, additionalData.articleId);
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
// ...
return true;
}
}
Additional Notes
NotificationServiceExtension
is an AndroidIntentService
so please do all your work synchronously. A wake lock is obtained so the device will not sleep while you're processing the payload.- By default
BigTextStyle
is set so to change the body text you must override the style with the following:
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
- If you plan on setting a big picture make sure to check for this before setting the
BigTextStyle
style above. - See Android's documentation on the NotificationCompat options.
- If you need to save data from push notifications see this great guide by Arthur Denner.
Updated over 1 year ago