Search code examples
javascriptreact-nativeibeacon

React Native (iOS): RCTEventEmitter before javascript is loaded


I have a problem implementing beacons in my app. I am using the library react-native-beacons-manager, but I think is a general "problem".

The issue is that when I kill my app (and this is important to reproduce the issue) and I get closer to my beacon, iOS fire an event regionDidEnter that is caught by a file wrote in native code, and then, sent to javascript using the method of RCTEventEmitter: [self sendEventWithName:@"regionDidEnter" body:event];

The problem is that this event is fired before javascript is fully loaded, so my listener:

// component.js
Beacons.BeaconsEventEmitter.addListener('regionDidEnter', b => {
        //code
      });

doesn't get called.

Order of events:

[BeaconsDemo] Did finish launching enter
[BeaconsDemo] Did finish launching After jsBundleURLForBundleRoot
[BeaconsDemo] Did finish launching After RCTRootView alloc
[BeaconsDemo] Did finish launching After UIWindow alloc
[BeaconsDemo] Did finish launching After makeKeyAndVisible
[BeaconsDemo] Did finish launching end
--iOS send the event and it is caught by RNiBeacon but it has no listeners yet--
[BeaconsDemo] no listeners in RnIBeacon.m
--Register
[BeaconsDemo] regionDidExit
-- First line of javascript --
[BeaconsDemo] start observing
[BeaconsDemo] requestAlwaysAuth

Any idea to handle this situation? Is there any way or approach to send the event through RCTEventEmitter waiting for the javascript is loaded?

Thanks


Solution

  • This general problem also exists when writing native beacon apps for iOS or Android. The solution is that you must set up your "hook" that enables beacon monitoring and adds the event listener (or delegate / notification callback as it is called in native code) in the appropriate place per platform:

    iOS: AppDelegate.didFinishLanching(options: )

    Android: Application.onCreate()

    The key is that this setup must be complete before those methods return.

    This same rule applies for ReactNative. The trick with ReactNative, is that by default JavaScript does not run on these events -- it runs only when a screen is launched, which sets up the hook too late for the above to work when your app is killed. To get this to work, you'll need to eject your app so you can set up some custom native code in the above callbacks.

    Two options:

    1. Implement beacon detection natively (easiest, but requires native coding on a per-platform basis)
    2. Add native code in the above native callbacks that starts a RCTBridge on iOS, and launches a view that executes code that triggers our JavaScript code to set up beacon detection. On Android, the equivalent would be to construct a new ReactRootView and ReactNativeInstanceManager in a way that it triggers your JavaScript code to set up beacon detection.

    I have not personally tested option 2 to see if it can set up the hooks soon enough. If it works, it will certainly be harder than the native solution, but allows you to keep your beacon detection logic in JavaScript.