Search code examples
javascriptobjective-cfacebookreact-nativebridge

How do you properly emit and receive an event from a bridged iOS module to JavaScript using React Native?


I have a React Native app and want to dispatch an event from Obj-C to test how the app event/emitting system works. I have read all the docs, thumbed through the react native repo for examples to follow and still don't know what is wrong with my code.

I know that my Obj-C fires because I've stepped through the code in Xcode and that self.bridge.eventDispatcher is not nil. I also know, via console logs that the main React Native component that is rendered in the simulator mounts and renders. The problem is that I don't see any console output for any of these notifications. Its possible that I'm requiring this ApplicationManager class incorrectly, or subscribing to the wrong "thing" to listen for events. It's possible I'm doing many things incorrectly/poorly :-)

Here's my Obj-C exported module:

@implementation ApplicationManager

RCT_EXPORT_MODULE();

@synthesize bridge = _bridge;

- (instancetype)init {
  self = [super init];
  if ( self ) {
    NSNotificationCenter * notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(applicationDidFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(applicationDidReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  }

  return self;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)applicationDidEnterBackground:(NSNotification *)notification {
  [self dispatchAppEventWithName:@"ApplicationDidEnterBackground" body:nil];
}

- (void)applicationWillEnterForeground:(NSNotification *)notification {
  [self dispatchAppEventWithName:@"ApplicationWillEnterBackground" body:nil];
}

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
  [self dispatchAppEventWithName:@"ApplicationDidFinishLaunching" body:nil];
}

- (void)applicationDidBecomeActive:(NSNotification *)notification {
  [self dispatchAppEventWithName:@"ApplicationDidBecomeActive" body:nil];
}

- (void)applicationWillResignActive:(NSNotification *)notification {
  [self dispatchAppEventWithName:@"ApplicationWillResignActive" body:nil];
}

- (void)applicationDidReceiveMemoryWarning:(NSNotification *)notification {
  [self dispatchAppEventWithName:@"ApplicationDidReceiveMemoryWarning" body:nil];
}

- (void)dispatchAppEventWithName:(NSString *)name body:(NSDictionary *)body {
  [self.bridge.eventDispatcher sendAppEventWithName:name body:body];
}

@end

I am aware that these events and notifications are already available in RCTAppState, but I'm trying to roll a solution myself to learn:

https://github.com/facebook/react-native/blob/2f6e7336cb96bf14d47554ae0db1836075ea809e/React/Modules/RCTAppState.m

I have tried using both:

[self.bridge.eventDispatcher sendAppEventWithName:name body:body];

and:

[self.bridge.eventDispatcher sendDeviceEventWithName:name body:body];

without any success. Other than the recipient class to use for those dispatched events on the JavaScript side, I'm not sure what the difference is between these two methods.

Here's the relevant React Native code:

var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS,
  NativeAppEventEmitter,
} = React;

// ... other code omitted for brevity ... 

componentDidMount: function() {
    console.log("main component didMount");
    debugger;
    NativeAppEventEmitter.addListener("ApplicationDidFinishLaunching", (reminder) => { console.log("app event emitter: ApplicationDidFinishLaunching:", reminder)} );
 },

The debugger line is hit in Chrome Debugger and NativeAppEventEmitter is not undefined, rather it is:

NativeAppEventEmitter
EventEmitter {_subscriber: EventSubscriptionVendor}_subscriber:
EventSubscriptionVendor__proto__: EventEmitter

My guess is that I'm doing something wrong, but I don't know what. Any help would be greatly appreciated.


Solution

  • Your code for subscribing to events and your code for sending events is fine.

    The problem you are having is because the ApplicationDidFinishLaunching event is fired off before componentDidMount where you are subscribing to the event.