React-native-onesignal: onOpened callback not called from cold start

Created on 5 Jul 2017  路  15Comments  路  Source: OneSignal/react-native-onesignal

Package Versions:
"react": "~15.4.0-rc.4",
"react-native": "0.40.0",
"react-native-onesignal": "^3.0.4"

Current Behavior
I've followed the Usage Instructions to the letter.

  • Whenever I open a notification with the app in the background, the callbacks work just fine.
  • Whenever I open a notification and the app is _not_ in the background, the callbacks don't get called at all.

Expected Behavior
Callbacks should be called regardless of whether the app is loaded from the background or cold-started.

Thoughts?

Edit: I just tried upgrading to 0.41.2 to be in sync with the example project. Problem persists.
Edit 2: This is my setup right now: https://gist.github.com/maxcodes/6d4657b4253e72e2be6fd391416f6cf3

Most helpful comment

Alright, I managed to get a workaround working. This is obviously a hack, so I'll leave this open to see what we can do about this.

From this comment I found the places where the native code (android and ios) emits the event. Then, I tried adding a delay before sending the event, as suggested here.

Android path: node_modules/react-native-onesignal/android/src/main/java/com/geektime/rnonesignalandroid/RNOneSignal.java
Leave the notifyNotificationOpened method like this:

// make sure you have this at the top of the file
import android.os.Handler;

    private void notifyNotificationOpened(Bundle bundle) {
        try {
            Handler handler = new Handler();
            // don't forget to add the "final" keyword here
            final JSONObject jsonObject = new JSONObject(bundle.getString("result"));
            handler.postDelayed(new Runnable() {
               @Override
               public void run() {
                 sendEvent("remoteNotificationOpened",  RNUtils.jsonToWritableMap(jsonObject));
               }
            }, 1500);
        } catch(Throwable t) {
            t.printStackTrace();
        }
    }

iOS path: node_modules/react-native-onesignal/ios/RCTOneSignal/RCTOneSignal.m
Change the handleRemoteNotificationOpened to this:

- (void)handleRemoteNotificationOpened:(NSString *)result {

    NSError *jsonError;
    NSData *objectData = [result dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                                         options:NSJSONReadingMutableContainers
                                                           error:&jsonError];
    double delayInSeconds = 1.5;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [curRCTBridge.eventDispatcher sendAppEventWithName:@"remoteNotificationOpened" body:json];
    });
}

You can try adjusting the delay to whatever works for you (the smaller the delay the better). This worked for me, I hope it helps someone else too 馃

All 15 comments

Alright, I managed to get a workaround working. This is obviously a hack, so I'll leave this open to see what we can do about this.

From this comment I found the places where the native code (android and ios) emits the event. Then, I tried adding a delay before sending the event, as suggested here.

Android path: node_modules/react-native-onesignal/android/src/main/java/com/geektime/rnonesignalandroid/RNOneSignal.java
Leave the notifyNotificationOpened method like this:

// make sure you have this at the top of the file
import android.os.Handler;

    private void notifyNotificationOpened(Bundle bundle) {
        try {
            Handler handler = new Handler();
            // don't forget to add the "final" keyword here
            final JSONObject jsonObject = new JSONObject(bundle.getString("result"));
            handler.postDelayed(new Runnable() {
               @Override
               public void run() {
                 sendEvent("remoteNotificationOpened",  RNUtils.jsonToWritableMap(jsonObject));
               }
            }, 1500);
        } catch(Throwable t) {
            t.printStackTrace();
        }
    }

iOS path: node_modules/react-native-onesignal/ios/RCTOneSignal/RCTOneSignal.m
Change the handleRemoteNotificationOpened to this:

- (void)handleRemoteNotificationOpened:(NSString *)result {

    NSError *jsonError;
    NSData *objectData = [result dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                                         options:NSJSONReadingMutableContainers
                                                           error:&jsonError];
    double delayInSeconds = 1.5;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [curRCTBridge.eventDispatcher sendAppEventWithName:@"remoteNotificationOpened" body:json];
    });
}

You can try adjusting the delay to whatever works for you (the smaller the delay the better). This worked for me, I hope it helps someone else too 馃

+1

I've created a branch with these changes, feel free to use it in the meantime.
https://github.com/maxcodes/react-native-onesignal/tree/delay

package.json

"react-native-onesignal": "git+https://github.com/maxcodes/react-native-onesignal.git#delay"

Thanks @maxcodes, this workaround works well enough for our needs 馃槃

Thanks @maxcodes, but for me only works for iOS, not for Android devices, any idea?

@jlopez90 try increasing the delay?

@maxcodes wow, that's awesome, saved my ass, thanks! Are there any plans add this fix to master?

Thanks @maxcodes increasing the delay worked!

@maxcodes Would it be possible to update your fork, so we can use it with RN0.47? No worries if you don't have time, just an open question.

@avishayil Are there any plans for fixing this on master? This would be really helpful, and would take the burden of @maxcodes from maintaining a fork. Pinging you since I saw you had most commits on this project, but feel free to pass through to other maintainers if that's more appropriate.

This workaround appears to be a little unstable: if the start of the application takes more time ? (old devices for example)
We can use:
Android: onReactContextInitialized override from ReactInstanceEventListener to send the event when the app is started
iOS: didFinishLaunchingWithOptions function

But I'm not very confortable with native codes for now, I don't even know if it's possible to do it like this

EDIT: this guy did a workaround too, he moved 'opened' listener outside any callback and it works !

There is no official way to fix it?

@GertjanReynaert I tried updating it a while back but I'm not too familiar with either Java or Obj-C, and I got stumped because upstream namespaced the names of the events.

@danieloprado something that we noticed recently is that this may be a symptom of your app taking too long to boot. For us, we were invoking the OneSignal listener in the componentDidMount of the main scene. However, the scene would wait for a very long query to get executed before mounting, so that's why the event is getting fired but not caught. See if you have a similiar situation and consider delaying the slow components to mount after the main scene has mounted.
TL;DR: It might be an implementation issue.

I hope that helps!

Just a heads up that I've posted a pretty lengthy comment at https://github.com/geektimecoil/react-native-onesignal/issues/332#issuecomment-341326624 discussing this problem.

I was having the same issue but adding OneSignal.configure({}); before declaring the listener functions solved it for me.

We have solved this problem by changing the /node_modules/react-native-onesignal/index.js. The problem is that OneSignal received the message before we add the opened listener. What we do is that we try listen before we start OneSignal and cache the message before we add opened listener.

'use strict';

import { NativeModules, NativeAppEventEmitter, NetInfo, Platform } from 'react-native';
import invariant from 'invariant';

var DEVICE_NOTIF_RECEIVED_EVENT = 'OneSignal-remoteNotificationReceived';
var DEVICE_NOTIF_OPENED_EVENT = 'OneSignal-remoteNotificationOpened';
var DEVICE_NOTIF_REG_EVENT = 'OneSignal-remoteNotificationsRegistered';
var DEVICE_IDS_AVAILABLE = 'OneSignal-idsAvailable';

 // ---------- CHANGE HERE --------------------
var openedHandler = false;
var lastOpenResult = false;
NativeAppEventEmitter.addListener(
    DEVICE_NOTIF_OPENED_EVENT,
    (result) => {
        console.log(result);
        if (openedHandler !==  false) {
            console.log("Got the result after the register");
            openedHandler(result);
        } else {
            console.log("Got the result before register");
            lastOpenResult = result;
        }
    }
);
// -------------------------------------

var RNOneSignal = NativeModules.OneSignal;

const _notifHandlers = new Map();

function handleConnectionStateChange(isConnected) {
    if (!isConnected) return;

    OneSignal.configure();
    NetInfo.isConnected.removeEventListener('connectionChange', handleConnectionStateChange);
}

NetInfo.isConnected.fetch().then(isConnected => {
    if (isConnected) return OneSignal.configure();
    NetInfo.isConnected.addEventListener('connectionChange', handleConnectionStateChange);
}).catch((...args) => console.warn("Error: ", args));


export default class OneSignal {

    static addEventListener(type: any, handler: Function) {

        // Listen to events of notification received, opened, device registered and IDSAvailable.

        invariant(
            type === 'received' || type === 'opened' || type === 'registered' || type === 'ids',
            'OneSignal only supports `received`, `opened`, `registered`, and `ids` events'
        );

        var listener;

        if (type === 'received') {
            listener = NativeAppEventEmitter.addListener(
                DEVICE_NOTIF_RECEIVED_EVENT,
                (notification) => {
                    handler(notification);
                }
            );
        } else if (type === 'opened') {
            // ---------- CHANGE HERE --------------------
            openedHandler = handler;
            if (lastOpenResult !== false) handler(lastOpenResult);
            console.log("Register the open event");
           // ------------------------------------------------
        } else if (type === 'registered') {
            listener = NativeAppEventEmitter.addListener(
                DEVICE_NOTIF_REG_EVENT,
                (notifData) => {
                    handler(notifData);
                }
            );
        } else if (type === 'ids') {
            listener = NativeAppEventEmitter.addListener(
                DEVICE_IDS_AVAILABLE,
                (ids) => {
                    handler(ids);
                }
            );
        }
        _notifHandlers.set(type, listener);

    }

   /// .....
}
Was this page helpful?
0 / 5 - 0 ratings