Intercept and customize push notifications before display on iOS and Android. Enable rich media, confirmed delivery, custom styling, and background data handling.
Notification service extensions let you intercept and modify push notifications before they are displayed to the user. Use them to add rich media, customize appearance, handle background data, and enable confirmed delivery analytics.
Create a class that implements INotificationServiceExtension and its onNotificationReceived method.The onNotificationReceived method receives an event parameter of type INotificationReceivedEvent.
package your.package.nameimport androidx.annotation.Keep;import com.onesignal.notifications.IActionButton;import com.onesignal.notifications.IDisplayableMutableNotification;import com.onesignal.notifications.INotificationReceivedEvent;import com.onesignal.notifications.INotificationServiceExtension;@Keeppublic class NotificationServiceExtension implements INotificationServiceExtension { @Override public void onNotificationReceived(INotificationReceivedEvent event) { IDisplayableMutableNotification notification = event.getNotification(); if (notification.getActionButtons() != null) { for (IActionButton button : notification.getActionButtons()) { // Modify action buttons here } } }}
The @Keep annotation is required in both Java and Kotlin to prevent code shrinking tools (R8 or ProGuard) from renaming or removing your class when minification is enabled.
If you call event.preventDefault() but never call display(), the notification is silently dropped. See Duplicated notifications for details on avoiding duplicate displays.
If you followed the Mobile SDK setup, the notification service extension is already configured. This section explains how to access the OneSignal notification payload data and troubleshoot issues.
Inside your didReceive(_:withContentHandler:) override, you call OneSignalExtension.didReceiveNotificationExtensionRequest to let OneSignal process the notification and invoke the content handler. Read or modify bestAttemptContent before calling this method.In this example, a notification is sent with the following data:
Access this additional data within the OneSignalNotificationServiceExtension via the a key inside the custom dictionary of userInfo:
if let bestAttemptContent = bestAttemptContent { if let customData = bestAttemptContent.userInfo["custom"] as? [String: Any], let additionalData = customData["a"] as? [String: Any] { if let jsonData = try? JSONSerialization.data(withJSONObject: additionalData, options: .prettyPrinted), let jsonString = String(data: jsonData, encoding: .utf8) { print("The additionalData dictionary in JSON format:\n\(jsonString)") } else { print("Failed to convert additionalData to JSON format.") } } if let messageData = bestAttemptContent.userInfo["aps"] as? [String: Any], let apsData = messageData["alert"] as? [String: Any], let body = apsData["body"] as? String, let title = apsData["title"] as? String { print("The message contents is: \(body), message headings is: \(title)") } else { print("Unable to retrieve apsData") } OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)}
Example console output:
The additionalData dictionary in JSON format:{ "additional_data_key_1" : "value_1", "additional_data_key_2" : "value_2"}The message contents is: The message contents, message headings is: The message title
1. Update the OneSignalNotificationServiceExtension code
Open NotificationService.m or NotificationService.swift and replace the file contents with the code below. This adds logging to help verify the extension is running.Replace YOUR_BUNDLE_ID with your actual Bundle ID.
import UserNotificationsimport OneSignalExtensionimport os.logclass 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)") os_log("%{public}@", log: OSLog(subsystem: "YOUR_BUNDLE_ID", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription) if let bestAttemptContent = bestAttemptContent { // Prepend "[Modified]" to confirm the extension is running print("Running NotificationServiceExtension") bestAttemptContent.body = "[Modified] " + bestAttemptContent.body OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler) } } override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent) contentHandler(bestAttemptContent) } }}
Debug log types need to be enabled in Console via Action > Include Debug Messages.
Why isn’t my notification service extension running on iOS?
The extension only runs when mutable-content is set in the push payload. OneSignal sets this automatically when you include an attachment or action buttons. Verify your Xcode settings match the requirements in the troubleshooting section.
Can I prevent a notification from displaying on Android?
Yes. Call event.preventDefault() in your onNotificationReceived method to suppress automatic display. You can then call event.getNotification().display() to show it later, or never call display() to silently drop the notification without displaying it. See Duplicated notifications for details on avoiding duplicates when using this pattern.
Yes. The @Keep annotation prevents ProGuard or R8 from renaming or removing your class that implements INotificationServiceExtension during minification. Without it, OneSignal cannot find the class at runtime.