|
| 1 | +Here you can find the guidelines for standard brownfield approach and advanced use cases. |
| 2 | + |
| 3 | +### Standard Brownfield |
| 4 | + |
| 5 | +As a golden rule of standard brownfield with react-native, your native App should never have to interact directly |
| 6 | +with react-native APIs. There are various reasons that doing the opposite is discouraged. |
| 7 | + |
| 8 | +- If you have different teams working on RN brownfield and the native App and you distribute your AAR/XCFramework to the native |
| 9 | +team. They can use the APIs from those artifacts and interact with them. However, if the native team have to import a react native |
| 10 | +API, say `PackageList` then the rule of brownfield is being violated. The native team have native developers and they should not need |
| 11 | +to worry about and interact with react-native directly. All of the abstraction should be handled within your artifacts. |
| 12 | + |
| 13 | +- If your native App interacts with react-native directly then you could imagine how complicated the codebase would be. The native App |
| 14 | +should follow and worry about their native APIs rather than interacting with react-native. If in future, some react-native APIs needs to |
| 15 | +be changed or refactored, then the effort would be cumbersome. On the contrast, if your native App was interacting with your artifact only |
| 16 | +then the native App need not to worry about what happens internally. This makes things simpler for the native App team. |
| 17 | + |
| 18 | +- If the native App team is using your artifact and any build, compile time or run time issue arises the stack trace would lead to your artifact |
| 19 | +and making it simpler for the teams to focus on their area only. On the contrary, if the native App team would interact with react-native directly, |
| 20 | +then any related issues would be time consuming for that team to figure out the root cause and then delegate to the team managing RN flows. |
| 21 | + |
| 22 | +Building upon the above points, below is how your brownfield implementation should look like if you're using `react-native-brownfield`: |
| 23 | + |
| 24 | +- In your brownfield android library or iOS xcframework, create a class following the facade pattern. The role of this class would be to encapsulate the |
| 25 | +initialization of `react-native-brownfield` by not asking the native App to interact with `react-native` directly. Below is how it would look like: |
| 26 | + |
| 27 | +```kt |
| 28 | +// Your artifact |
| 29 | +import com.facebook.react.PackageList |
| 30 | +import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative |
| 31 | + |
| 32 | +class ReactNativeHostManager { |
| 33 | + companion object { |
| 34 | + fun initialize(application: Application) { |
| 35 | + loadReactNative(application) // imported from autogenerated ReactNativeApplicationEntryPoint |
| 36 | + val packages = PackageList(application).packages // imported from autogenerated PackageList |
| 37 | + |
| 38 | + ReactNativeBrownfield.initialize(application, packages) |
| 39 | + } |
| 40 | + } |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +Then the native App only needs to call `initialize` like so: |
| 45 | + |
| 46 | +```kt |
| 47 | +// native App |
| 48 | +ReactNativeHostManager.initialize(application) |
| 49 | +``` |
| 50 | + |
| 51 | +If you do not follow this approach, then the usage in the native App would look like this: |
| 52 | + |
| 53 | +```kt |
| 54 | +// native App |
| 55 | +import com.facebook.react.PackageList |
| 56 | +import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative |
| 57 | + |
| 58 | + |
| 59 | +loadReactNative(application) |
| 60 | +val packages = PackageList(this).getPackages() |
| 61 | +ReactNativeHostManager.initialize(application) |
| 62 | +``` |
| 63 | + |
| 64 | +As you can see the issue here is that now we are mixing the native App with react-native APIs, which is discouraged. If we stick to |
| 65 | +the above approach then the native App is free of interacting with react-native directly. |
| 66 | + |
| 67 | + |
| 68 | +### Advanced Use Cases: |
| 69 | + |
| 70 | +If you built on top of the above approach, then you can rather easily scale it to incorporate advanced use cases. We will discuss here only one |
| 71 | +case but you can extend it to your need as the gist remains the same. |
| 72 | + |
| 73 | +Consider you need to present a few native App's existing screens from react-native. Which means you need a communication way so that you can tell |
| 74 | +the native App to present this screen. Let's see how we can achieve this: |
| 75 | + |
| 76 | +In your `ReactNativeHostManager` add the following method: |
| 77 | + |
| 78 | +```kt |
| 79 | +// Your artifact |
| 80 | +class ReactNativeHostManager { |
| 81 | + companion object { |
| 82 | + fun createView( |
| 83 | + context: Context, |
| 84 | + activity: FragmentActivity?, |
| 85 | + moduleName: String, |
| 86 | + launchOptions: Bundle? = null, |
| 87 | + eventHandler: (String) -> Unit = {} |
| 88 | + ): FrameLayout { |
| 89 | + EventHandlerRegistry.register(moduleName, eventHandler) // Later invoke this event or callback to perform the navigation |
| 90 | + return ReactNativeBrownfield.shared.createView(context, activity, moduleName, launchOptions) |
| 91 | + } |
| 92 | + } |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +What happens here is that the `createView` method now accepts an optional callback or eventHandler argument. The native App will rely on this eventHandler |
| 97 | +to perform the navigation or to receive any events from the react-native side. The usage in the native App would look like below: |
| 98 | + |
| 99 | +```kt |
| 100 | +// native App |
| 101 | +ReactNativeHostManager.createView(context, activity, "Enterprise") { |
| 102 | + if (it == "navigate_to_faq") { |
| 103 | + // present faq fragment |
| 104 | + } |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +HINT: To achieve this you will need to write some native code in your artifact to trigger an event from JS and then forward that event by invoking the eventHandler. You can wire up a native module and expose it to JS. Now, when you need to present FAQ screen from the native App, you invoke that native module method which forwards the event to eventHandler. The gist is below: |
| 109 | + |
| 110 | +```JS |
| 111 | +// Your artifact |
| 112 | + |
| 113 | +// handlerId is the name of RN module loaded, eg: Enterprise |
| 114 | +RNEventHandler.sendEvent("navigate_to_faq", handlerId); |
| 115 | +``` |
| 116 | + |
| 117 | +```kt |
| 118 | +// Your artifact |
| 119 | + |
| 120 | +// RNEventHandler |
| 121 | +fun sendEvent(event: String, handlerId: String) { |
| 122 | + EventHandlerRegistry.sendEvent(handlerId, event) |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +```kt |
| 127 | +// Your artifact |
| 128 | + |
| 129 | +// EventHandlerRegistry |
| 130 | +fun sendEvent(event: String, handlerId: String) { |
| 131 | + eventHandlers[handlerId].invoke(event) |
| 132 | +} |
| 133 | +``` |
0 commit comments