RN Native Module Setup

This guide shows how to pass data from the Android Notification Service Extension Native Java class to React Native.

This follows upon concepts listed in the React Native Docs:

Jump to:

Android Notification Service Extension Module

Let's create a native module, NotificationServiceExtensionModule, that will allow you to pass Notification Data received in the Android’s Notification APIs to JavaScript while the app is running but not in focus.

📘

Requirements

You must have setup the Android Notification Service Extension before continuing with this integration.

🚧

Will not work if app is not running in the background or foreground.

If the app is swiped away, the React Native Module will not be initialized so the notification data within the NotificationServiceExtension cannot bridge into React Native.

Create a Notification Service Extension Module

Create the NotificationServiceExtensionModule.java Java file inside android/app/src/main/java/com/your-app-name/ folder. This Java file will contain your native module Java class.

3560

Add the following code:

package com.your-app-name; // replace com.your-app-name with your app’s name

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;
import android.util.Log;

public class NotificationServiceExtensionModule extends ReactContextBaseJavaModule {
    NotificationServiceExtensionModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "NotificationServiceExtensionModule";
    }

    @ReactMethod
    public void createNotificationServiceExtensionEvent(String name) {
        Log.d("NotificationServiceExtensionModule", "Create event called with name: " + name);
    }
    
}

Register the Module (Android Specific)

Add your Native Module to ReactPackage by creating a new Java Class named MyAppPackage.java that implements ReactPackage inside the android/app/src/main/java/com/your-app-name/ folder:

Then add the following content:

package com.your-app-name; // replace your-app-name with your app’s name

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyAppPackage implements ReactPackage {

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new NotificationServiceExtensionModule(reactContext));

        return modules;
    }

}

To register the NotificationServiceExtensionModule package, you must add MyAppPackage to the list of packages returned in ReactNativeHost's getPackages() method. Open up your MainApplication.java file, which can be found in the following path: android/app/src/main/java/com/your-app-name/MainApplication.java

Locate ReactNativeHost’s getPackages() method and add your package to the packages list getPackages() returns:

@Override
  protected List<ReactPackage> getPackages() {
    @SuppressWarnings("UnnecessaryLocalVariable")
    List<ReactPackage> packages = new PackageList(this).getPackages();
    // below MyAppPackage is added to the list of packages returned
    packages.add(new MyAppPackage());
    return packages;
  }
3648

Sending Events to Javascript

The below instructions pick up from the React Native Docs: https://reactnative.dev/docs/native-modules-android#sending-events-to-javascript

Update the NotificationServiceExtensionModule.java to include the event emitter

package com.your-app-name; // replace your-app-name with your app’s name

import androidx.annotation.Nullable;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;
import android.util.Log;

import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;


public class NotificationServiceExtensionModule extends ReactContextBaseJavaModule {
    public static NotificationServiceExtensionModule instance;

    NotificationServiceExtensionModule(ReactApplicationContext context) {
        super(context);
        instance = this;
    }

    @Override
    public String getName() {
        return "NotificationServiceExtensionModule";
    }

    @ReactMethod
    public void createNotificationServiceExtensionEvent(String name) {
        Log.d("NotificationServiceExtensionModule", "Create event called with name: " + name);
    }

    private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }

    public void emitNotificationEvent() {
        Log.i("OneSignalExample", "Emit Notification Event");
        WritableMap params = Arguments.createMap();
        params.putString("eventProperty", "someValue");
        sendEvent(getReactApplicationContext(), "NotificationEvent", params);
    }
}

Update the NotificationServiceExtension.java file to add:

package com.your-app-name; // replace your-app-name with your app’s 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;

import com.your-app-name.NotificationServiceExtensionModule;


@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 -> builder.setColor(context.getResources().getColor(R.color.colorPrimary)));

        JSONObject data = notification.getAdditionalData();
        Log.i("OneSignalExample", "Received Notification Data: " + data);

        if (NotificationServiceExtensionModule.instance != null ) {
            NotificationServiceExtensionModule.instance.emitNotificationEvent();
        }

        // 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);
    }
}

In your Javascript App.js file include:

// Used to access data from NotificationServiceExtension
import { NativeEventEmitter, NativeModules } from 'react-native';

  // sending events to Javascript: https://reactnative.dev/docs/native-modules-android#sending-events-to-javascript 
  const eventEmitter = new
  NativeEventEmitter(NativeModules.NotificationServiceExtensionModule);
  this.eventListener = eventEmitter.addListener('NotificationEvent', (event) => {
    console.log("NotificationEvent Received In JS: ", event.eventProperty)// "someValue"
  });

iOS Notification Service Extension Module

Let's create a native module, NotificationServiceExtensionModule, that will allow you to pass Notification Data received in the iOS's Notification APIs to JavaScript while the app is running but not in focus.

📘

Requirements

You must have setup the iOS Notification Service Extension as directed in the React Native & Expo SDK Setup guide you followed when adding OneSiganl before continuing with this integration.

🚧

Will not work if app is not running in the background or foreground.

If the app is swiped away, the React Native Module will not be initialized so the notification data within the NotificationServiceExtension cannot bridge into React Native.

Create a Notification Service Extension Module

In Xcode, open your app's xcworkspace file. Right click the main project folder and create a new Objective-C h file called NotificationServiceExtensionModule

1484 1484

The NotificationServiceExtensionModule.h file should contain this code.

#import <foundation/Foundation.h>

// NotificationServiceExtensionModule.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface NotificationServiceExtensionModule : RCTEventEmitter <RCTBridgeModule>

+ (NotificationServiceExtensionModule*) sharedInstance;
- (void)emitNotificationEvent:(NSDictionary *)userInfo;


@end

Within the same root project folder. Create an Objective-C .m file called NotificationServiceExtensionModule.

1484 1484 1484

The NotificationServiceExtensionModule.m file should look like this:

#import <Foundation/Foundation.h>

// NotificationServiceExtensionModule.m
#import "NotificationServiceExtensionModule.h"

@implementation NotificationServiceExtensionModule

static NotificationServiceExtensionModule* _instance = nil;


+(NotificationServiceExtensionModule*) sharedInstance {
//    @synchronized( _instance ) {
//        if( !_instance ) {
//            _instance = [[NotificationServiceExtensionModule alloc] init];
//        }
//    }
    
    return _instance;
}

// To export a module named NotificationServiceExtensionModule
RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  NSLog(@"Supported EVENTS__________________________");
  _instance = self;
  return @[@"NotificationEvent"];
}

- (void)emitNotificationEvent:(NSDictionary *)userInfo
{
  NSString *eventName = userInfo[@"aps"];
  [self sendEventWithName:@"NotificationEvent" body:@{@"aps": eventName}];
}

@end

Sending Events to Javascript

Update AppDelegate to use NotificationServiceExtensionModule to send event to JavaScript

Your AppDelegate.h file should look something like this:

#import <Foundation/Foundation.h>
#import <EXUpdates/EXUpdatesAppController.h>
#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>

#import <UserNotifications/UserNotifications.h>


#import <UMCore/UMAppDelegateWrapper.h>

@interface AppDelegate : UMAppDelegateWrapper <UIApplicationDelegate, RCTBridgeDelegate, EXUpdatesAppControllerDelegate>


@end

Your AppDelegate.m file should implement the didReceiveRemoteNotification method like this:

#import "NotificationServiceExtensionModule.h"
  
 //Other AppDelegate code ...at bottom before @end place

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  //access NotificationServiceExtensionModule emitNotificationEvent method
  [NotificationServiceExtensionModule.sharedInstance emitNotificationEvent:userInfo ];
  completionHandler(UIBackgroundFetchResultNoData);
}

In your Javascript App.js file include (same as the Android setup):

// Used to access data from NotificationServiceExtension
import { NativeEventEmitter, NativeModules } from 'react-native';

   // sending events to Javascript: https://reactnative.dev/docs/native-modules-android#sending-events-to-javascript 
    const eventEmitter = new NativeEventEmitter(NativeModules.NotificationServiceExtensionModule);
    this.eventListener = eventEmitter.addListener('NotificationEvent', (event) => {
      console.log("NotificationEvent Received In JS: ", event.eventProperty) // "someValue"
    });

👍

Done!

Make sure to send push with content_available to wake the app when it is in the background.