Send event to js from swift or objective-c

I created the following class (compressed version), heres a link to the full file https://github.com/cotyembry/CastRemoteNative/blob/7e74dbc56f037cc61241f6ece24a94d8c52abb32/root/ios/CastRemoteNative/NativeMethods.swift

@objc(NativeMethods) class NativeMethods: RCTEventEmitter { @objc(sendEventToJSFromJS) func sendEventToJSFromJS { self.emitEvent(eventName: "test", body: "bodyTestString") } func emitEvent(eventName: String: body: Any) { self.sendEvent(withName: eventName, body: body) } } 

This works fine and fires my callback listener, which is in my javascript code, when I call the emitEvent method as follows: its modified fragment from https://github.com/cotyembry/CastRemoteNative/blob/7e74dbc56f037cc61241f6ece24a94d8c52j32/32 Components / ChromecastDevicesModal.js

Javascript side

 import { NativeModules, NativeEventEmitter } from 'react-native' //here I bring in the swift class to use inside javascript var NativeMethods = NativeModules.NativeMethods; //create an event emitter to use to listen for the native events when they occur this.eventEmitter = new NativeEventEmitter(NativeMethods); //listen for the event once it sends this.subscription = this.eventEmitter.addListener('test', (body) => { console.log('in test event listener callback', body)}); NativeMethods.sendEventToJSFromJS() //call the native method written in swift 

I just have a sendEventToJSFromJS method that sendEventToJSFromJS called when a button is clicked in javascript

This works again, and the console.log('in test event listener callback', body) code works and works on the javascript side

My question is where this does NOT work:

If, after defining the class, I had to do the following in a fast file, this will not work:

 var nativeMethodsInstance = nativeMethods() nativeMethodsInstance.sendEventToJSFromSwift() 

Why? Because the following error occurs:

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

So, when creating instance NativeMethods, unlike no ... what is the difference?

For more information:

Objective-C gets the same bridge that doesn't pose the problem when I write the same code snippets in .h and .m files, and not in .swift files.

I found where the error message is printed in native code, but it just has a variable

 _bridge 

and checks if it is nil

Files of this error come from:

 RCTEventEmitter.h RCTEventEmitter.c 

here is the full fragment of the RCTEventEmitter.c file

 - (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 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" method:@"emit" args:body ? @[eventName, body] : @[eventName] completion:NULL]; } else { RCTLogWarn(@"Sending `%@` with no listeners registered.", eventName); } } 

Where is this _bridge value set and how is it set, so I can know, in cases where it does not work, how to set it

I found the following in RCTEventEmitter.h

 @property (nonatomic, weak) RCTBridge *bridge; 

The above error mentions that the bridge is inherited in RCTEventEmitter, so maybe the problem is with the weak part in the bridge property?

Or do I need to change my strategy in the way I do all this?

I know that this probably should be due to me, not fully understanding

 @synthesize bridge = _bridge; 

part of the code and all languages ​​that mix do not help much lol ...

It is really difficult, so any help would be greatly appreciated! Thanks so much for your time.

here is a link to the full project when the project history code represented the code from my question above (since I have made changes to the project since):

https://github.com/cotyembry/CastRemoteNative/tree/7e74dbc56f037cc61241f6ece24a94d8c52abb32

+5
source share
1 answer

I get it

Warning : this solution uses an outdated method that responds to its own method - I could not figure out how to "correctly" inherit from RCTEventEmitter and send an event ... every time I tried _bridge would be nil

Make sure Swift is connected to Objective-C (if you use swift to send events to javascript)

Do not create instances of exported Native modules (regardless of whether they are written in Swift or Objective-C)

Let the React Native base implementation do this and for each class that should send an event, export this specific Objective-C implementation code for the native class or Swift code (native module) in React-Native. This allows javascript to listen to the event.

 var publicBridgeHelperInstance = PublicBridgeHelper() //instantiate the the objective c class from inside the .swift file to use later when needing to get a reference to the bridge to send an event to javascript written in react native @objc(DeviceManager) //export swift module to objective c class DeviceManager: NSObject { @objc(deviceDidComeOnline:) //expose the function to objective c public func deviceDidComeOnline(_ device: GCKDevice) { //imagine this deviceDidComeOnline function gets called from something from the Native code (totally independent of javascript) - honestly this could be called from a native button click as well just to test it works... //emit an event to a javascript function that is a in react native Component listening for the event like so: //1. get a reference to the bridge to send an event through from Native to Javascript in React Native (here is where my custom code comes in to get this to actually work) let rnBridge = publicBridgeHelperInstance.getBridge() //this gets the bridge that is stored in the AppDelegate.m file that was set from the `rootView.bridge` variable (more on this later) //(if you want to print the bridge here to make sure it is not `nil` go ahead: print("rnBridge = \(rnBridge)") //2. actually send the event through the eventDispatcher rnBridge?.eventDispatcher().sendAppEvent(withName: "test", body: "testBody data!!!") } } 

in AppDelegate.h put (in addition to the code that was already in the file)

 #import "YourProjectsBridgingHeaderToMakeThisCodeAvailableInSwift.h" //replace this with your actual header you created when creating a swift file (google it if you dont know how to bridge swift to objective c) @interface PublicBridgeHelper: NSObject -(RCTBridge*)getBridge; @end 

in AppDelegate.m put (in addition to the code that was already in the file)

 #import <React/RCTRootView.h> RCTBridge *rnBridgeFromRootView; @implementation PublicBridgeHelper //this is created to SIMPLY return rnBridgeFromRootView defined above over to my Swift class when actually sending the event to javascript that defines a react native Component -(RCTBridge*)getBridge { NSLog(@"rnBridgeFromRootView = @%@", rnBridgeFromRootView); return rnBridgeFromRootView; } 

important - also be sure to add the following line of code to the Objective Ch header to make this PublicBridgeHelper definition available for use in .swift code

 #import "AppDelegate.h" 

, finally,

now to show you how to set the rnBridgeFromRootView variable used in AppDelegate.m (which is returned and used in .swift code right before sending the event to javascript)

open AppDelegate.m and in the method body

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

include the following after the line of code that instantiates the rootView variable

i.e. after a line that probably looks like

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

add:

 rnBridgeFromRootView = rootView.bridge //set the bridge to be exposed and returned later and used by the swift class 

Now, to explain the part of publicBridgeHelperInstance.getBridge() that is in the .swift file

PublicBridgeHelper is an instance of the c object class, which allows the class’s high-speed ability to get a reference to its own native bridge

If you still have problems understanding my answer after reading this, I made a video on it and you can watch it here:

https://www.youtube.com/watch?v=GZj-Vm9cQIg&t=9s

+1
source

Source: https://habr.com/ru/post/1266399/


All Articles