-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What is the method can be used to send an event from native module to JS? #8714
Comments
I figured it out by reading its source code. Using the 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 In JavaScript side, you have to use the import { NativeModules, NativeEventEmitter } from 'react-native'
const myModuleEvt = new NativeEventEmitter(NativeModules.MyModule)
myModuleEvt.addListener('sayHello', (data) => console.log(data)) |
First issue, shouldn't:
Be:
Second issue: RCTEventEmitter.m is throwing an error:
_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 {
Sorry, I'm a bit confused how to deal sending event since this deprecation. |
@andybangs
But,I added |
@purplepeng it looks like you are subclassing RCTEventEmitter, but actually using a promise to interact with React Native. Are you successfully listening to your |
@andybangs
|
@purplepeng any word on how to do this on the android side? |
bridge still nil. What is wrong with this?
|
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... |
@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 I have 2 question tho. Do you start listening on the Objective C event in the Thanks again |
@andybangs Just tried to replace my implementation with yours and I keep having this:
|
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 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 (
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? |
This entire thread has been helpful, appreciate it, but I'm not quite there yet. |
@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 // 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 |
@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:
class FooModule: RCTEventEmitter {
...
}
@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: |
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 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 I'm also not sure if I used the As mentioned before the |
@sandromartis you should have your "MyOtherModule" inherit RCTEventEmitter (you kind of did with the EventEmitter class already). Then you can use There must be something missing from the code example you've given because I don't understand why |
@chourobin The thing is that I cannot make 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 I assume that if you don't have an 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 https://gist.github.com/chourobin/f83f3b3a6fd2053fad29fff69524f91c#file-events-ui-md |
I subclassed |
Finally, I got the solution.
When you use it!
|
I fix this problem by:
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.
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 (ce6fb33, 53d5504) Can someone comment on this? |
This may help you.
Finally use it!! You must ensure that the name is already registered. |
@ZionChang Great piece of code!! |
@ZionChang this looks really good but how do you call the instance method |
@ZionChang I read the code for RCTEventEmitter and saw that the My issue is I'm emitting the event before the listener was attached! Working on it... |
@niksoper no need to call |
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
along with
this is the native objective-c side:
this setup will give me the following result: |
@ZionChang but with no listeners the event will be useless and My reading of the RCTEventEmitter source tells me that
|
I know that, like I said, such as you can call |
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.This issue indicates that
sendDeviceEventWithName
should work but actually it's also deprecated.What is the proper way to send an event to JS?
The text was updated successfully, but these errors were encountered: