Search code examples
iosswiftreact-nativereact-native-bridge

React Native IOS Bridge Native View - proper way to call Native Method from JS side


I want to discuss the options i have come across for calling a native method from a IOS ViewManager/View in JS side without using properties but direct method calls.

Option A:

implementing a method in the ViewManager which searches for the correct view and calls the given method in the view, like this:

func callMethodViaManager(_ node:NSNumber) {
DispatchQueue.main.async {
let myView = self.bridge.uiManager.view(forReactTag: node) as! MyView
myView.myMethod()
  }
}

and then in the JS side implement a handler like this:

 const handleSomething = (e) => { 
  UIManager.dispatchViewManagerCommand(
  ReactNative.findNodeHandle(ref.current),
  UIManager.SwiftComponent.Commands.callMethodViaManager,
  [])
 };

This is just a short summary of the relevant parts, the process in full can be seen in full detail maybe just a bit old but with some adjustments one can get it to work also with functional components:

https://medium.com/@jjdanek/react-native-calling-class-methods-on-native-swift-views-521faf44f3dc

Option B:

For this option let's go with the best scenario which is that one can get all the necessary data, setup, etc on the ViewManager ready via delegates for example or some other pattern or Swift sugar.

Calling the Native methods in the ViewManager from the JS side directly with the use of NativeModules, like this:

 const handleSomething = (e) => { 
 NativeModules.MyViewManager.myMethod()
 };

I could not find much about this option in correlation to a Native View being bridged, this way of doing it is used for Native Module bridging explicitly. The only things i could find where:

React Native UI Component Method

or guides like these one:

https://teabreak.e-spres-oh.com/swift-in-react-native-the-ultimate-guide-part-1-modules-9bb8d054db03#4377

I have tried both methods and at first glance they seem to work both.

So my questions are:

Why is Option A the better solution/option and the one which is recommended or most used?

What is wrong or can go wrong with Option B?

Is there anything else to take into consideration?

Is there a better way which is not trough properties?


Solution

  • Option B is more flexible.

    If you use Option A, then you can't pass Promise and Callback parameter to the native side.

    It seems possible in iOS But not in Android.

    This is a related issue.

    React Native bridge API is all confusing

    There is no guide about how to call the native UI method with Promise or Callback.

    There is a example about how to pass Promise to native side with calling native UI instance method.

    SketchView... is just my example module name.

    class SketchViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
        override fun getName() = "SketchViewModule"
    
        private fun runCommandWithPromise(
            viewId: Int, promise: Promise, handler: (view: SketchView) -> Unit
        ) {
            reactApplicationContext.getNativeModule(UIManagerModule::class.java)?.let { uiManager ->
                uiManager.addUIBlock {
                    (it.resolveView(viewId) as? SketchView)?.let { view ->
                        handler(view)
                    } ?: run {
                        promise.safeReject("[runCommandWithPromise] Cannot find View with UIManager")
                    }
                }
            }
        }
    ...