Mobile Service Extensions
Using the iOS and Android Notification Service Extension in your mobile apps.
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.
1. Create a class for the Service Extension
Create a class that extends INotificationServiceExtension
and implement the onNotificationReceived
method.
The method onNotificationReceived
parameter is event
of type INotificationReceivedEvent .
package your.package.name
import com.onesignal.notifications.IActionButton;
import com.onesignal.notifications.IDisplayableMutableNotification;
import com.onesignal.notifications.INotificationReceivedEvent;
import com.onesignal.notifications.INotificationServiceExtension;
@Keep // Keep is required to prevent minification from renaming or removing your class
public class NotificationServiceExtension implements INotificationServiceExtension {
@Override
public void onNotificationReceived(INotificationReceivedEvent event) {
IDisplayableMutableNotification notification = event.getNotification();
if (notification.getActionButtons() != null) {
for (IActionButton button : notification.getActionButtons()) {
// you can modify your action buttons here
}
}
// this is an example of how to modify the notification by changing the background color to blue
notification.setExtender(builder -> builder.setColor(0xFF0000FF));
//If you need to perform an async action or stop the payload from being shown automatically,
//use event.preventDefault(). Using event.notification.display() will show this message again.
}
}
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
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
This guide is for debugging issues with Images, Action Buttons, or Confirmed Deliveries not showing on iOS mobile apps.
Check your Xcode Settings
In your OneSignalNotificationServiceExtension > General tab, make sure you have the correct "Supported Destinations" and your "Minimum Deployment" is set to iOS 10 or higher.
- If you are using Cocoapods make sure these match with your main target to avoid build errors.
Continuing in the OneSignalNotificationServiceExtension > Info tab, 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
.
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 UserNotifications
import OneSignalExtension
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 {
/* DEBUGGING: Uncomment the 2 lines below to check this extension is executing
Note, this extension only runs when mutable-content is set
Setting an attachment or action buttons automatically adds this */
print("Running NotificationServiceExtension")
bestAttemptContent.body = "[Modified] " + bestAttemptContent.body
OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: 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 {
OneSignalExtension.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];
//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.Debug setLogLevel:ONE_S_LL_VERBOSE];
[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
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 - Select Start
Send this device a notification with a message (contents
).
You should see a message logged with the app running and not running.
If you do not see a message, then remove OneSignal from your app and follow our Mobile SDK Setup again to verify you setup OneSignal correctly.
Updated about 2 months ago