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](https://github.com/OneSignal/OneSignal-Android-SDK/blob/25924dc3739fbe3ae64a73efc7b504449a18cdea/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/notifications/INotificationReceivedEvent.kt#L46).

package your.package.name

import com.onesignal.notifications.IActionButton;
import com.onesignal.notifications.IDisplayableMutableNotification
import com.onesignal.notifications.INotificationReceivedEvent;
import com.onesignal.notifications.INotificationServiceExtension;

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.

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:

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.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
import UserNotifications
import OneSignalFramework
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.Debug.setLogLevel(.LL_VERBOSE)
          OneSignal.Debug.setAlertLevel(.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.