A bank wants to add React Native screens to its existing native iOS and Android apps; how do you embed React Native, and what are the main pitfalls?
answer
- native projects move under ios/ and android/
- AppRegistry name is the contract
- RCTReactNativeFactory on iOS
- ReactHost and ReactActivity on Android
- Metro in debug, embedded bundle in release
basics
~20 sAdd React Native to the native apps, register a root component with AppRegistry, and host it natively: RCTReactNativeFactory on iOS, a ReactHost with a ReactActivity or ReactFragment on Android. Watch names, release bundling and minimum OS versions.
solid answer
~50 sThe documented integrated approach puts a JavaScript root (a `package.json` taken from the Community Template) above the existing apps, moved into `ios/` and `android/`. iOS adds React Native through the template's `Podfile` and `pod install`; Android applies the `com.facebook.react.settings` plugin with `autolinkLibrariesFromCommand()` in `settings.gradle`, and the `com.facebook.react` plugin plus `autolinkLibrariesWithApp()` in the app module. JavaScript registers a screen with `AppRegistry.registerComponent('Payments', () => App)`. iOS shows it through `RCTReactNativeFactory` and its `rootViewFactory`; Android makes the `Application` a `ReactApplication` exposing a `ReactHost`, then hosts the screen in a `ReactActivity` or `ReactFragment`. Pitfalls: the registered name must match on both sides, release builds must embed the JS bundle instead of loading it from Metro (on iOS that needs a bundling build phase added by hand), the host apps must meet React Native's minimums (iOS 15.1, Android minSdk 24), and the native team may prefer a prebuilt isolated package over a Node toolchain.
code
swift · 31 linesimport UIKit
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
class PaymentsViewController: UIViewController {
var reactNativeFactory: RCTReactNativeFactory?
var reactNativeFactoryDelegate: RCTReactNativeFactoryDelegate?
override func viewDidLoad() {
super.viewDidLoad()
reactNativeFactoryDelegate = ReactNativeDelegate()
reactNativeFactoryDelegate!.dependencyProvider = RCTAppDependencyProvider()
reactNativeFactory = RCTReactNativeFactory(delegate: reactNativeFactoryDelegate!)
view = reactNativeFactory!.rootViewFactory.view(withModuleName: "Payments")
}
}
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
override func sourceURL(for bridge: RCTBridge) -> URL? {
self.bundleURL()
}
override func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}
}go deeper
Recall that React Native can be added to an existing native app for a single screen, registered in JavaScript with AppRegistry.
Explain the integration steps on both platforms: dependencies, Gradle and CocoaPods setup, the JavaScript entry, and the native host classes.
Anticipate the pitfalls: name mismatches, release bundling, minimum OS versions, navigation and auth seams, and native build ownership.
Choose integrated or isolated delivery for the organisation, and define how native and React Native teams share releases and responsibility.
## What brownfield means A **brownfield** app is an existing native app whose entry point is not React Native; React Native is added for one screen, one flow or one feature. A bank with mature Swift and Kotlin apps adding React Native payment screens is the classic case. The React Native docs cover it in *Integration with Existing Apps*, separately from starting a new project. ## The integrated approach, step by step 1. **Directory structure.** Create a new root folder, put a `package.json` from the Community Template in it, and move the existing projects into `ios/` and `android/`. 2. **JavaScript dependencies.** Install `react-native` and the template's dependencies; `node_modules` is ignored by git. 3. **iOS.** Use the template's `Podfile` as the reference (adjusting its target name), then `bundle exec pod install`. 4. **Android.** In `settings.gradle`, include the React Native Gradle plugin, apply `com.facebook.react.settings` and call `autolinkLibrariesFromCommand()`. In the app's `build.gradle`, apply `com.facebook.react`, depend on `react-android` and `hermes-android`, and call `autolinkLibrariesWithApp()` in the `react` block. 5. **JavaScript entry.** An `index.js` registers each screen: `AppRegistry.registerComponent('Payments', () => App)`. 6. **Native hosts.** Show the registered component from native code (below). ## Hosting the screen | | iOS | Android | |---|---|---| | Runtime setup | an `RCTReactNativeFactory` created with a delegate subclassing `RCTDefaultReactNativeFactoryDelegate` | the `Application` implements `ReactApplication`, exposes `reactHost` from `getDefaultReactHost(...)` and calls `SoLoader.init` in `onCreate` | | Showing a screen | `factory.rootViewFactory.view(withModuleName: "Payments")` in a view controller | a `ReactActivity` whose `getMainComponentName()` returns `"Payments"`, or a `ReactFragment` built with `ReactFragment.Builder()` | | Initial data | the `initialProperties` overload of the view factory | launch options, for example `ReactFragment.Builder().setLaunchOptions(...)` | | Bundle location | `RCTBundleURLProvider` (Metro) in `DEBUG`, `main.jsbundle` in release | Metro in debug, the bundled asset in release | ## Pitfalls - **Name mismatch.** The string in `registerComponent` and the one the native host requests must be identical, or the screen fails with `"Payments" has not been registered`. - **Release bundles.** Debug builds fetch JavaScript from Metro; release builds must embed the bundle. On Android the React Native Gradle plugin bundles it into the APK or App Bundle; on iOS a brownfield app must add a *Bundle React Native code and images* run-script build phase by hand. Test a release build before handing it to QA. - **Minimum versions.** React Native 0.87 requires iOS 15.1 and Android minSdk 24, and uses the New Architecture only; an older host app must raise its deployment targets first. - **Theme on Android.** The React Native activity needs a theme without an ActionBar, or an ActionBar is drawn above the screen. - **Navigation seams.** Back navigation, deep links and authentication state cross the native and JavaScript boundary; decide which side owns each before building. - **Build ownership.** Native engineers now need Node.js, CocoaPods and the React Native Gradle plugin in their workflow. ## The isolated alternative Expo's brownfield guide describes a second approach: build the React Native part separately and ship it to the native apps as a prebuilt library, an **AAR** for Android and an **XCFramework** for iOS. Native teams then consume it like any other dependency and never touch Node.js. It fits organisations with separate native and React Native teams, at the cost of a packaging pipeline and slower iteration across the boundary. ## Choosing between them Choose **integrated** when one team works on both sides and iterates on them together. Choose **isolated** when native teams must stay free of the JavaScript toolchain, or release cadences differ. Either way, start with one low-risk screen, measure startup and memory on real devices, and only then widen the scope. For a bank, that first screen is better a read-only one, such as a statement view, than a payment flow, so the integration is proven before it carries money.
- How do you pass the signed-in customer's ID to the React Native screen?Pass it as initial properties when creating the root view: on iOS through the `initialProperties` overload of `rootViewFactory.view(withModuleName:)`, on Android through launch options such as `ReactFragment.Builder().setLaunchOptions(...)`. The root component receives them as props, so the first render needs no extra native call.
- When would the isolated approach beat the integrated one for the bank?When the native teams must not adopt Node.js and CocoaPods or Gradle plugin changes, or ship on a different cadence. Packaging the React Native part as an AAR and an XCFramework lets them consume it like any SDK, at the cost of a packaging pipeline and slower cross-boundary iteration.
saying these in an interview costs you the question
- Brownfield apps must be rewritten as React Native apps first
- The registered component name can differ from the name native requests
- Release builds still load JavaScript from the Metro dev server
- A brownfield host can keep the legacy architecture enabled on 0.87
- Any iOS deployment target works with React Native 0.87