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. |
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 & Upgraded SDK
Must be using OneSignal SDK Versions:
- Android 4.0.0+
- React Native 4.0.0+
- Flutter 3.0.0+
- Cordova/Ionic 3.0.0+
- Unity 3.0.0+
- Xamarin 4.0.0+
See Mobile SDKs API Migration Guides to upgrade the OneSignal SDK.
Step 1. Create a class for the Service Extension
Create a class that extends OSRemoteNotificationReceivedHandler
and implement the remoteNotificationReceived
method.
The method remoteNotificationReceived
parameters are context
of type Context
and notificationReceivedEvent
of type OSNotificationReceivedEvent
.
package your.package.name
import android.content.Context;
import android.util.Log;
import org.json.JSONObject;
import com.onesignal.OSNotification;
import com.onesignal.OSMutableNotification;
import com.onesignal.OSNotificationReceivedEvent;
import com.onesignal.OneSignal.OSRemoteNotificationReceivedHandler;
@SuppressWarnings("unused")
public class NotificationServiceExtension implements OSRemoteNotificationReceivedHandler {
@Override
public void remoteNotificationReceived(Context context, OSNotificationReceivedEvent notificationReceivedEvent) {
OSNotification notification = notificationReceivedEvent.getNotification();
// Example of modifying the notification's accent color
OSMutableNotification mutableNotification = notification.mutableCopy();
mutableNotification.setExtender(builder -> {
// Sets the accent color to Green on Android 5+ devices.
// Accent color controls icon and action buttons on Android 5+. Accent color does not change app title on Android 10+
builder.setColor(new BigInteger("FF00FF00", 16).intValue());
// Sets the notification Title to Red
Spannable spannableTitle = new SpannableString(notification.getTitle());
spannableTitle.setSpan(new ForegroundColorSpan(Color.RED),0,notification.getTitle().length(),0);
builder.setContentTitle(spannableTitle);
// Sets the notification Body to Blue
Spannable spannableBody = new SpannableString(notification.getBody());
spannableBody.setSpan(new ForegroundColorSpan(Color.BLUE),0,notification.getBody().length(),0);
builder.setContentText(spannableBody);
//Force remove push from Notification Center after 30 seconds
builder.setTimeoutAfter(30000);
return builder;
});
JSONObject data = notification.getAdditionalData();
Log.i("OneSignalExample", "Received Notification Data: " + data);
// If complete isn't call within a time period of 25 seconds, OneSignal internal logic will show the original notification
// To omit displaying a notification, pass `null` to complete()
notificationReceivedEvent.complete(mutableNotification);
}
}
Step 2. Add the following to your AndroidManifest.xml
.
AndroidManifest.xml
.Add OneSignal class name and your class value as meta-data
within the AndroidManifest.xml
file under the application tag. Ignore any "unused" warnings.
<application ...>
<!-- name doesn't change, value = your class fully name spaced-->
<meta-data android:name="com.onesignal.NotificationServiceExtension"
android:value="com.onesignal.example.NotificationServiceExtension" />
</application>
Getting a payload from a notification
Major Release SDKs (4.x.x) see the OSNotification class
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. |
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:
- Media Attachments (images, video, & audio clip)
- Action Buttons - Action buttons are still possible but have to be hard coded in the app with an iOS notification category.
- Confirmed Deliveries - Requires the NSE + App Groups
- Badges cannot be incremented but can be "set to" a value.
- 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 basic text only notifications are working but you are having issues with Images, Action Buttons, or Confirmed Deliveries not showing on iOS mobile apps follow this guide.
Checking your Xcode Settings
1. Ensure Deployment iOS Version & Types
Select the OneSignalNotificationServiceExtension under General then "Deployment Info" ensure you select the platforms and a low enough iOS version for the device you are testing with.
- If you are using Cocoapods make sure these match with your main target to avoid build errors.
2. Ensure Correct Plist Entries
Select the OneSignalNotificationServiceExtension under Info expand the NSExtension
key. Ensure you see:
Swift
Raw plist syntax for reference:
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
Objective-C
Same expect instead of $(PRODUCT_MODULE_NAME).NotificationService
Objective-C should be NotificationService
.
3. Turn off "Copy only when installing"
Select your main target this time, go to "Build Phases" and expand "Embed App Extensions". Ensure "Copy only when installing" is NOT checked, uncheck it if it is:
How to debug the Notification Service Extension not running
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.
Make sure to replace YOUR_BUNDLE_ID
with your actual bundle id.
#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];
//If your SDK version is < 3.5.0 uncomment and use this code:
/*
[OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest
withMutableNotificationContent:self.bestAttemptContent];
self.contentHandler(self.bestAttemptContent);
*/
NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.YOUR_BUNDLE_ID.onesignal"];
NSLog(@"NSE player_id: %@", [userDefault stringForKey:@"GT_PLAYER_ID"]);
NSLog(@"NSE app_id: %@", [userDefault stringForKey:@"GT_APP_ID"]);
/* 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];
[OneSignal setLogLevel:ONE_S_LL_VERBOSE visualLevel:ONE_S_LL_NONE];
[OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest
withMutableNotificationContent:self.bestAttemptContent
withContentHandler:self.contentHandler];
}
- (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
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
self.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: "YOUR_BUNDLE_ID", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription)
if let bestAttemptContent = bestAttemptContent {
OneSignal.setLogLevel(.LL_VERBOSE, visualLevel: .LL_NONE)
bestAttemptContent.body = "[Modified] " + bestAttemptContent.body
OneSignal.didReceiveNotificationExtensionRequest(self.receivedRequest, with: self.bestAttemptContent, withContentHandler: self.contentHandler)
}
}
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)
}
}
}
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.
FAQ
How can I setup Service Extensions to get data in React Native?
You will need to use React Native Native Modules in this case. See our guide on RN Native Module Setup.
Updated almost 2 years ago