React-native: What is the method can be used to send an event from native module to JS?

Created on 12 Jul 2016  ·  56Comments  ·  Source: facebook/react-native

According to React Native documentation, you can use sendAppEventWithName to send an event from native code to JS. But in my XCode, code suggestions tell me that this method is deprecated.

image

This issue indicates that sendDeviceEventWithName should work but actually it's also deprecated.

What is the proper way to send an event to JS?

Locked

Most helpful comment

I figured it out by reading its source code. Using the RCTEventEmitter class.

MyModule.h

#import "RCTEventEmitter.h"
#import "RCTBridgeModule.h"

@interface MyModule : RCTEventEmitter <RCTBridgeModule>

@end
MyModule.m

@implementation MyModule

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
  return @[@"sayHello"];
}

- (void)tellJS () {
   [self sendEventWithName:@"sayHello" body:@"Hello"];
}

@end

So you can send an event called sayHello with data Hello to JavaScript by calling the tellJS method.

In JavaScript side, you have to use the NativeModules module to get this native module and wrap it in NativeEventEmitter class so that you can receive events.

import { NativeModules, NativeEventEmitter } from 'react-native'

const myModuleEvt = new NativeEventEmitter(NativeModules.MyModule)
myModuleEvt.addListener('sayHello', (data) => console.log(data))

All 56 comments

I figured it out by reading its source code. Using the RCTEventEmitter class.

MyModule.h

#import "RCTEventEmitter.h"
#import "RCTBridgeModule.h"

@interface MyModule : RCTEventEmitter <RCTBridgeModule>

@end
MyModule.m

@implementation MyModule

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
  return @[@"sayHello"];
}

- (void)tellJS () {
   [self sendEventWithName:@"sayHello" body:@"Hello"];
}

@end

So you can send an event called sayHello with data Hello to JavaScript by calling the tellJS method.

In JavaScript side, you have to use the NativeModules module to get this native module and wrap it in NativeEventEmitter class so that you can receive events.

import { NativeModules, NativeEventEmitter } from 'react-native'

const myModuleEvt = new NativeEventEmitter(NativeModules.MyModule)
myModuleEvt.addListener('sayHello', (data) => console.log(data))

@livoras

First issue, shouldn't:

 [sendEventWithName:@"sayHello" body:@"Hello"];

Be:

 [self sendEventWithName:@"sayHello" body:@"Hello"];

Second issue: RCTEventEmitter.m is throwing an error:

- (void)sendEventWithName:(NSString *)eventName body:(id)body
{
  RCTAssert(_bridge != nil, @"bridge is not set. This is probably because you've "
            "explicitly synthesized the bridge in %@, even though it's inherited "
            "from RCTEventEmitter.", [self class]);

  if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) {
    RCTLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`",
                eventName, [self class], [[self supportedEvents] componentsJoinedByString:@"`, `"]);
  }
  if (_listenerCount > 0) {
    [_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit"
                      args:body ? @[eventName, body] : @[eventName]];
  } else {
    RCTLogWarn(@"Sending `%@` with no listeners registered.", eventName);
  }
}

_bridge is nil and it's throwing an exception. Should we open another ticket on fixing the documentation on the proper way to do this? I think it's still not fully clear.

I have the same problem, the bridge is nil. Have you resolve it? @joekim

@purplepeng I ended up just creating a native module and using callbacks instead of using events. Haven't tested it again recently.

I got it to work in swift... here is the snippets

// in my module...
@objc(YourModule)
class YourModule: RCTEventEmitter {


  override func supportedEvents() -> [String]! {
    return ["UploadProgress"]
  }

further down in the class

    // A progress event occurred, notify the user...
    uploadTask.observeStatus(.Progress) { (snapshot:FIRStorageTaskSnapshot) in

      if let progress = snapshot.progress {

        let percentComplete =  Double(progress.completedUnitCount) / Double(fs)

        let ret : [String:AnyObject] = [
          "completedUnitCount": String(progress.completedUnitCount),
          "percentComplete": String(percentComplete),
          "totalUnitCount": String(fs)
        ]

        self.sendEventWithName("UploadProgress", body: ret )
      }
    }

In my react-native code

    const myModuleEvt = new NativeEventEmitter(NativeModules.YourModule)
    var subscription = myModuleEvt.addListener(
      'UploadProgress',
      (progress) => {
        console.log("UploadProgress")
        console.log(JSON.stringify(progress, null, 2))
      }
    );

@joekim @aaronksaunders @livoras Thank you. It works for me. I realized the process of sent event from native to JS in two separate module before, so it doesn't work.

so , the start of 'send event from iOS to javascript' is still javascript,am i right? 3Q

what about android? this seems to create module scoped events, but how do we do this in android?

@purplepeng can you explain what you meant by "I realized the process of sent event from native to JS in two separate module before, so it doesn't work."

I am trying to send an event to JS and keep seeing the error that the bridge is not set.

@aaronksaunders thanks for tip but how to deal a viewmanager in the same time?

@objc(RNAnalogClockSwift)
class RNAnalogClockManager: RCTViewManager, RCTEventEmitter {

throws: Multiple inheritance from classes 'RCTViewManager' and 'RCTEventEmitter'

Sorry, I'm a bit confused how to deal sending event since this deprecation.
Previous way was far easier to figure out and implement.

@andybangs
I have resolved the problem with following code.

#import "RCTBridgeModule.h"
#import "RCTEventEmitter.h"

@interface RCTUserManager : RCTEventEmitter <RCTBridgeModule>
@end
@implementation RCTUserManager

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  return @[AuthorizationWasFailedNotification,AuthorizationWasSucceedNotification,AuthorizationWasInvalidNotification,AuthorizationWasCancelledNotification];
}

RCT_EXPORT_METHOD(signinWithUserName:(NSString *)userName
                  password:(NSString *)password
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  [self.userModel signinWithUserName:userName
                            password:password
                          completion:^(STIHTTPResponseError *e) {
                            if ( e ) { 
                              NSError * error;
                              reject([NSString stringWithFormat:@"%@",@(e.code)],e.message,error);
                            }
                            else
                            {
                              NSDictionary * data = @{@"token":self.userModel.token,@"user":[self.userModel.user JSONStringRepresentation]};
                              resolve([data JSONStringRepresentation]);
                              [self sendEventWithName:AuthorizationWasSucceedNotification body:[data JSONStringRepresentation]];
                            }
                          }];
}

But,I added
[self sendEventWithName:AuthorizationWasSucceedNotification body:[data JSONStringRepresentation]];
in other module before,not in this RCTUserManager,so it didn't work. Hope can help you.

@purplepeng it looks like you are subclassing RCTEventEmitter, but actually using a promise to interact with React Native. Are you successfully listening to your [self sendEventWithName:body:]; call in JS, or just relying on your promise resolve block? Or both?

@andybangs
Both. In my react-native code, the part of event as below:

componentDidMount() {
    const authModuleEvent = new NativeEventEmitter(NativeModules.UserManager)
    signinSubscription = authModuleEvent.addListener('AuthorizationWasSucceedNotification',
     (reminder) => {
     ...
});
}
componentWillUnmount() {
    signinSubscription.remove();
}

@purplepeng any word on how to do this on the android side?

bridge still nil. What is wrong with this?

#import "RCTBridgeModule.h"
#import "RCTEventEmitter.h"

@interface HelperManager : RCTEventEmitter <RCTBridgeModule>

- (void)sendURLSchemeParameters:(NSURL *)url;

@end
#import "HelperManager.h"

@implementation HelperManager

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
  return @[@"URL_SCHEME"];
}

RCT_EXPORT_METHOD(sendURLSchemeParameters:(NSURL *)url) {
  [self sendEventWithName:@"URL_SCHEME" body:@{}];
}
@end

Can we please have an insight on how to handle this now? I made a bridge using the doc but knowing that this method is deprecated, I'm not really confident...
I think this needs to be documented as this means every lib using this are going to break when the update removing the support of the deprecated methods is going to land!

@lazarte @julien-rodrigues: Here is a stripped down version of a functioning subclass of RCTEventEmitter in my app: https://gist.github.com/andybangs/c4651a3916ebde0df1c977b220bbec4b

@andybangs Thanks a lot for your gist. I'm a complete noob in Objective C but this is exactly what I'm trying to achieve.

I'm receiving a Firebase dynamic link on application's openUrl and I need to be able to fire an event with the "decoded" link that my RN bridge would listen and forward it to the JS side.

I have 2 question tho. Do you start listening on the Objective C event in the init of your class?
And when do you fire the method to the NotificationCenter?

Thanks again

@andybangs Just tried to replace my implementation with yours and I keep having this:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'bridge is not set. This is probably because you've explicitly synthesized the bridge in TestClass, even though it's inherited from RCTEventEmitter.

@julien-rodrigues

  1. I expose a startListening method to be called in a RN component’s componentWillMount method
  2. My subclass of RCTEventEmitter handles firing notifications

To continue with my example above, I am not explicitly instantiating my GSEventEmitter class anywhere because RN creates the instance for me. I import the GSEventEmitter header file in whatever other class I want to emit an event from to use it. Here is another stripped down gist of how I am doing this: https://gist.github.com/andybangs/5f1ee78247b0346e24d55119c6a02afa

Hope this helps, and let me know if you have any other questions.

@andybangs Thanks a lot for your time 👍

Yes that's really more clear to me now lol
This implementation is different from what I've done and I understand now why I got conflicts.

I was trying to go full native. I was attaching the listener by myself in AppDelegate and then fired the event when the FirebaseSDK was done computing the incoming link. Which then fired the JS event. So either the event was fired before the JS side was listening or the JS handler was not attached on the right instance(?, don't know if that's possible).

Either way I like your idea, I'm going to try it out. Thanks for sharing

Facing the same problem, subclassed the RCTEventEmitter but stuck on the bridge is nil assertion error (bridge is not set. This is probably because you've explicitly synthesized the bridge in ReactNativeEventManager, even though it's inherited from RCTEventEmitter.).

#import <React/RCTEventEmitter.h>

@interface ReactNativeEventManager : RCTEventEmitter

-(void)postEvent:(NSNotification *)notification;

@end

#import "ReactNativeEventManager.h"

@implementation ReactNativeEventManager
RCT_EXPORT_MODULE();

-(void)postEvent:(NSNotification *)notification {
    NSString *eventName = notification.userInfo[@"name"];
    [self sendEventWithName:eventName body:[notification userInfo]];
}

- (NSArray<NSString *> *)supportedEvents {
    return @[@"add-exchange", @"open-exchange"];
}

@end

Anybody an idea on how to solve this? Did try all of the above solutions, but none of them seem to work. :(

@VincentdeWit94 did you check out the gists I posted above that utilize NSNotificationCenter?
Example subclass of RCTEventEmitter: https://gist.github.com/andybangs/c4651a3916ebde0df1c977b220bbec4b
Example usage: https://gist.github.com/andybangs/5f1ee78247b0346e24d55119c6a02afa

This entire thread has been helpful, appreciate it, but I'm not quite there yet.
@andybangs if I'm not mistaken you created a .m file just for raising the event (handleBeaconSightingNotification); then you import that into your .m file that will emit the event calling the GSEventEmitter method???

@TheoGit that sounds about right. It may not be how usage is shown in the docs, but is the solution I'm currently relying on to get past that "bridge is not set..." error that others have also posted about here.

My subclass of RCTEventEmitter, GSEventEmitter, is modeled after how RCTLinkingManager in the RN codebase subclasses RCTEventEmitter. Although the code itself is not up to date, the idea to try this came from the accepted answer of this SO post: http://stackoverflow.com/questions/36092903/listening-for-events-in-react-native-ios.

In my application, GSEventEmitter handles all native events that need to be emitted to JS, so I import and use GSEventEmitter.h in a number of other classes. In the example above I use it in GSBeaconManager.m to emit beacon sightings with the following call:

[GSEventEmitter application:[UIApplication sharedApplication] beaconSighted:beaconID];

Here is another example:

I already have native code for registering notification settings, so instead of using the RN PushNotificationIOS abstraction, I use my Objective-C code and a method I define in GSEventEmitter called + (BOOL)application:(UIApplication *)application notificationsRegistered:(UIUserNotificationSettings *)notificationSettings; that I call in my AppDelegate and listen for in JS.

// AppDelegate.m
...
- (void)application:(UIApplication *)application 
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
  [GSEventEmitter application:[UIApplication sharedApplication] 
      notificationsRegistered:notificationSettings];
}
...
// Welcome.js
...
componentWillMount() {
  const { GSEventEmitter } = NativeModules;
  const { NOTIFICATIONS_REGISTERED, SIGHTED_BEACON } = GSEventEmitter;
  const { addBeacon } = this.props.actions;

  this.eventEmitter = new NativeEventEmitter(GSEventEmitter);
  this.eventEmitter.addListener(NOTIFICATIONS_REGISTERED, this.handleNotificationsRegistered);
  this.eventEmitter.addListener(SIGHTED_BEACON, (data) => addBeacon(data.payload));
}

componentDidMount() {
  const { GSNotificationManager } = NativeModules;
  GSNotificationManager.registerNotifications();
}

componentWillUnmount() {
  this.eventEmitter.remove();
}

handleNotificationsRegistered() {
  const { GSBeaconManager } = NativeModules;
  GSBeaconManager.startListening();
}
...

@andybangs I really appreciate the time/explanation - will do my best to reproduce this; the only part confusing for me is the delegate (GMBLBeaconManagerDelegate) - thank you again!

@TheoGit: No problem. I included the GMBLBeaconDelegate protocol only as an example of how I am using my subclass of RCTEventEmitter. In that example, the method – beaconManager:didReceiveBeaconSighting: tells the instance of GMBLBeaconManager when a beacon is sighted. The relevant part to this discussion, is the call in that method [GSEventEmitter application:[UIApplication sharedApplication] beaconSighted:beaconID]; which emits an event that I am listening for in JS.

@livoras Thank you. It works for me.

@andybangs this is working; I had to make a few tweaks (like NativeAppEventEmitter instead of NativeEventEmitter) but overall it was your suggestion of subclassing RCTEVentEmitter that I finally understood after reading and re-reading; thanks again this thread as been invaluable.

@andybangs your example worked for me.

@andybangs
Actually, no need to use NotificationCenter, RCTBridge has a method called module(for: AnyClass),
which can get an instance of RCTEventEmitter, then you can call your fuction to send event

@hitbear518 Can you give more insights on how to use module(for: ) and get the instance of RCTEventEmitter? Thanks a lot

@xaviraol Here's what i did:

  1. subcass RCTEventEmitter
class FooModule: RCTEventEmitter {
...
}
  1. export it to js
@interface RCT_EXTERN_MODULE(FooModule, NSObject)
...
@end

3.get the RCTBridge(I saved it as singleton), then get the module to send event

if let fooModule = MyRCTBridgeDelegate.sharedInstance.bridge.module(for: FooModule) as? FooModule {
    fooModule.sendMyEvent()
}

then you can subscript the event from js code

Also, checkout the master branch doc in react-native official site for more info:
Sending Events to JavaScript

thanks @hitbear518 !!!

@hitbear518 would it be possible to provide a more complete example of your implementation?

@aaronksaunders How are you instantiating the RCTEventEmitter (YourModule)? I have my code pretty much exactly the same as yours, but if I instantiate MyEventEmitter, the bridge variable is null and the app crashes. I've read that I should "let react instiatate modules" but I'm not quite clear on how to do this. It seems possible in ObjeC but how to do it in Swift?? I'm missing something simple I'm sure, any help would be appreciated.

@hitbear518 I make it out,thanks.

@aaronksaunders You shouldn't instantiate your emitter yourself. Look here.

"When you used the macro RCT_EXPORT_MODULE() React-Native will instantiate the class for you, and any subsequent alloc/inits will create new instances, unrelated the original. The bridge will not be instantiated in these new instances."

The solution is to override startObserving and stopObserving and listen to NSNotifications.

I used both Objective-C and Swift to test, but I still get "_bridge is nil and it's throwing an exception"

Here's what I did:

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

@interface RNEventManager : RCTEventEmitter <RCTBridgeModule>

@end

// RNEventManager.m
#import "RNEventManager.h"

@implementation RNEventManager

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  return @[@"ExportDescriptionViewWillAppear"];
}

@end

And I called sendEventWithName:body: in viewWillAppear of ExportDescription.

So did I miss something?

Okay, I seem to have managed to get it to work using Swift. It's almost the same as @hitbear518 proposed.

EventEmitter.swift

@objc(EventEmitter)
class EventEmitter: RCTEventEmitter {

  @objc
  override func supportedEvents() -> [String] {
    return ["event1", "event2"]
  }
}

EventEmitterBridge.m

#import "React/RCTEventEmitter.h"

@interface RCT_EXTERN_MODULE(EventEmitter, RCTEventEmitter)
@end

And then in my other module I'm doing something like this:

MyOtherModule.swift

var bridge: RCTBridge!

@objc(MyOtherModule)
class MyOtherModule {

  func sendSomeEvent() {
    if let eventEmitter = bridge.module(for: EventEmitter.self) as? EventEmitter {
          self.sendEvent(withName: "event1", body: "Woot!")
    }
  }
}

I don't have a single clue how and why this is working since my iOS / Swift knowledge is basically non-existent, but apparently, the var bridge: RCTBridge! object gets somehow synthesized (is that what this is called?) so it is available in MyOtherModule.

I'm also not sure if I used the module(for:) thingy correctly but it seems to work.

As mentioned before the EventEmitter should not be instantiated manually.

@sandromartis you should have your "MyOtherModule" inherit RCTEventEmitter (you kind of did with the EventEmitter class already). Then you can use sendEvent from there.

There must be something missing from the code example you've given because I don't understand why bridge: RCTBridge! is available in MyOtherModule.

@chourobin The thing is that I cannot make MyOtherModule inherit from RCTEventEmitter because it is already inheriting from RCTViewManager.
That's why I created a new module EventEmitter that inherits from RCTEventEmitter which creates the whole problem of accessing it in MyOtherModule.
So MyOtherModule actually looks like this:

var bridge: RCTBridge!

@objc(MyOtherModule)
class MyOtherModule: RCTViewManager {

  func sendSomeEvent() {
    if let eventEmitter = bridge.module(for: EventEmitter.self) as? EventEmitter {
          self.sendEvent(withName: "event1", body: "Woot!")
    }
  }
}

Now that I'm writing this I realize that bridge: RCTBridge! is available in MyOtherModule because that one is inheriting from RCTViewManager. Also bridge: RCTBridge! is actually not necessary and you can just use self.bridge.module(for: EventEmitter.self).

I assume that if you don't have an RCTViewManager you probably have to set the bridge to a global value in AppDelegate.m to make this work. Something like this:

_AppDelegate.h_

@property (nonatomic, strong) RCTBridge *bridge;

_AppDelegate.m_

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  ...

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"LayerTwo"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];

  bridge = rootView.bridge;

  ...

}

@end

Let me know if that makes sense or if I'm talking complete bullshit here. I'm totally new to all this react-native and native iOS development stuff.

@sandromartis nope that makes sense. just fyi, there is another api for sending events from ui-based modules, and it's by using RCTBubblingEventBlock or RCTDirectEventBlock. I had a gist that summarizes some of this info if it's helpful:

https://gist.github.com/chourobin/f83f3b3a6fd2053fad29fff69524f91c#file-events-ui-md

I subclassed RCTEventEmitter for a background timer and for some reason the events fire in the background in the emulator but not on my real iOS device. Does anyone know why this happens? sendDeviceEventWithName works perfectly but it sends a deprecation warning.

Finally, I got the solution.

#import "RNNotification.h"
@implementation RNNotification

RCT_EXPORT_MODULE();

+ (id)allocWithZone:(NSZone *)zone {
    static RNNotification *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}

- (NSArray<NSString *> *)supportedEvents
{
    return @[@"EventReminder"];
}

- (void)sendNotificationToReactNative
{
    [self sendEventWithName:@"EventReminder" body:@{@"name": @"name"}];
}

When you use it!

RNNotification *notification = [RNNotification allocWithZone: nil];
[notification sendNotificationToReactNative]

I fix this problem by:

  • (NSArray *)supportedEvents {
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    delegate.MyViewController = self;

return @[@"CancelEvent", @"OKEvent"];
}

Globally , call delegate.MyViewController 's function to send events;

@Liqiankun Your solution Works like a charm . Much Appreciated :)

This doesn't seem to work on versions before 0.47.0.
Its easily reproducible:

  • Make a new app that supports 0.46.0: react-native init TestProject --version [email protected]
  • Use @LiangQiao's singleton to emit events
  • Nothing gets received on JS layer

Tested on the same app with version 0.49.0 and it works like a charm.

I could be wrong but I believe it has to do with these breaking changes (https://github.com/facebook/react-native/commit/ce6fb337a146e6f261f2afb564aa19363774a7a8, https://github.com/facebook/react-native/commit/53d5504f4077bf7fb7cbf7c1edac7e2cbde5c964)

Can someone comment on this?

This may help you.

BKLEventEmitter.h

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

@interface BKLEventEmitter : RCTEventEmitter <RCTBridgeModule>
+ (void)emitEventWithName:(NSString *)name andPayload:(NSDictionary *)payload;
@end

BKLEventEmitter.m

@implementation BKLEventEmitter

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
    // register name, may be more...
    return @[@"NotificationName"];
}

- (void)startObserving {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    for (NSString *notificationName in [self supportedEvents]) {
        [center addObserver:self
               selector:@selector(emitEventInternal:)
                   name:notificationName
                 object:nil];
    }
}

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

- (void)emitEventInternal:(NSNotification *)notification {
    [self sendEventWithName:notification.name
                   body:notification.userInfo];
}

+ (void)emitEventWithName:(NSString *)name andPayload:(NSDictionary *)payload {
    [[NSNotificationCenter defaultCenter] postNotificationName:name
                                                    object:self
                                                  userInfo:payload];
}

@end

Finally use it!! You must ensure that the name is already registered.
[BKLEventEmitter emitEventWithName:@"NoticationName" andPayload:nil];

@ZionChang Great piece of code!!
Unfortunately, it was my ignorance that left out an integral piece of code in the new 0.46.0 RN app I made. Regardless, I like the static method calls much more than having an extra object. Much appreciated

@ZionChang this looks really good but how do you call the instance method startObserving? I'm pretty new to Objective-C so it's not clear to me.

@ZionChang I read the code for RCTEventEmitter and saw that the startObserving method is called when you call addListener from JavaScript.

My issue is I'm emitting the event before the listener was attached!

Working on it...

@niksoper no need to call addListener which is called in react-native source code, just call emitEventWithName.

just starting out using events to communicate from objective-c to js. this is all pretty confusing at the moment. I have a "working" version but I don't understand it since I have to use NativeEventEmitter and DeviceEventEmitter to receive events from objective-c:

const communicationModuleEmitter = new NativeEventEmitter(CommunicationModule);
const subscription = communicationModuleEmitter.addListener(
  'genericLogEvent',
  (e) => console.log('got a genericLogEvent: ', e),  // <--- this is NOT called but somehow needed
);

along with

const subscribeForNativeEvents = (eventID, callback) => {
  LOG_S().d('Subscribing to ' + eventID);
  DeviceEventEmitter.addListener(eventID, callback);
}; 
subscribeForNativeEvents('genericLogEvent', (event) => { 
  console.log('got a generic event!!!', event); // <--- this is actually called
});

this is the native objective-c side:

RCT_EXPORT_METHOD(foo)
{
  RCTLogInfo(@"foo");
  [self emitMessageToRN:@"genericLogEvent" :@{ @"logMessage": @"event from foo" }];
}
- (void) emitMessageToRN: (NSString *)eventName :(NSDictionary *)params {
  [self sendEventWithName: eventName body: params];
}

this setup will give me the following result:
D got a generic event!!! "type is: object : {"logMessage":"event from foo"}"
anyone knows why I need to subscribe both with DeviceEventEmitter and NativeEventEmitter? I tried to find documentation on this but haven't found a lot.

@ZionChang but with no listeners the event will be useless and emitEventWithName will go nowhere.

My reading of the RCTEventEmitter source tells me that startObserving will only be called once you've added a listener in JavaScript - which is presumably an optimisation:

RCT_EXPORT_METHOD(addListener:(NSString *)eventName)
{
  if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) {
    RCTLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`",
                eventName, [self class], [[self supportedEvents] componentsJoinedByString:@"`, `"]);
  }
  _listenerCount++;
  if (_listenerCount == 1) {
    [self startObserving];
  }
}

I know that, like I said, such as you can call [BKLEventEmitter emitEventWithName:@"NoticationName" andPayload:nil]; in viewDidAppear which is a native method of UIViewController. And method named addListener will be called by react-native automatically. You can have a try and make a breakpoint to test it.
@niksoper

Was this page helpful?
0 / 5 - 0 ratings