Seamlessly Integrate React Native Modules into Existing Native Apps with Pegasus
This article explains how to embed React Native components into an existing iOS/Android app by turning the RN module into a native library called Pegasus, covering integration steps, architecture, and bidirectional communication between JavaScript and native code.
We wrapped a React Native component with a
UIViewController(iOS) or
Activity(Android) as the first step to integrate Pegasus into the Baixing main app. This article details Pegasus’s design and implementation, focusing on two questions: how to integrate Pegasus into an existing native project and how to handle communication between RN and native.
Integration
Integrating an RN module into an existing app generally follows these steps (see React Native docs):
Create RN dependencies and directory structure.
Write your component in JavaScript.
Install RN packages via NPM/Yarn and native dependencies via CocoaPods (iOS) or Gradle (Android).
Add
RCTRootView(iOS) or
ReactRootView(Android) to the app as a container for the RN component.
Test and verify the code.
Bundle the JavaScript.
These steps require a local RN development environment (Node, NPM, etc.), which can be burdensome for native‑only developers.
<code>npm install
npm run start
</code>To avoid polluting the native toolchain, we propose turning Pegasus into a native library distributed via CocoaPods (iOS) or Gradle (Android). For iOS, adding a single line to
Podfilepulls in Pegasus:
<code>pod 'Pegasus'
</code>In native code, a simple initialization launches an RN page:
<code>// Initialize Pegasus
Pegasus *pegasus = [[Pegasus alloc] init];
// Create RN component container
PEGComponentViewController *viewController = [PEGComponentViewController alloc] initWithPegasus:pegasus
moduleName:@"Profile"
initialProperties:@{ @"name" : "Jack" }];
[self.navigationController pushViewController:viewController animated:YES];
</code>Pegasus’s architecture is illustrated below:
Externally Pegasus appears in three forms:
An NPM package (JavaScript).
A CocoaPod for iOS.
A Gradle dependency for Android.
Internally it consists of four layers:
Native UI Components and Module APIs – custom native UI and APIs that supplement RN’s built‑in components.
JavaScript layer – pure React code handling UI and business logic.
Native Public API – view controllers, activities, and initialization interfaces exposed to the host app.
Cross‑platform shell project – tooling for development and debugging.
For iOS we package the bundled JS, assets, and Objective‑C code into a single CocoaPod:
Key configuration files (trimmed for brevity) are shown below:
<code>// package.json
{
"name": "pegasus",
"dependencies": {
"react": "16.0.0-alpha.12",
"react-native": "0.46.4",
"react-native-code-push": "^4.1.0-beta"
}
}
</code> <code># Pegasus.podspec
require 'json'
npm_package = JSON.load(File.read(File.expand_path('../package.json', __FILE__)))
Pod::Spec.new do |s|
s.name = 'Pegasus'
s.version = npm_package['version']
s.summary = 'React Native Components used by Baixing.'
s.source_files = 'ios/Classes/**/*.{h,m}'
s.resource_bundles = { 'Pegasus' => ['ios/Assets/{Pegasus.js,assets,*.xcassets,*.lproj}'] }
react_native_version = npm_package['dependencies']['react-native'].sub('^', '~>')
s.dependency 'React/BatchedBridge', react_native_version
s.dependency 'React/Core', react_native_version
# ... other RN dependencies ...
s.dependency 'CodePush/Core', code_push_version
s.dependency 'SSZipArchive'
s.dependency 'MBProgressHUD'
end
</code>After resolving native dependencies (often requiring a private CocoaPods or Maven repository), we bundle the JavaScript:
<code>// NPM script
"scripts": {
"bundle-ios": "react-native bundle --platform ios --dev false --entry-file index.ios.js --bundle-output ios/Assets/Pegasus.js --sourcemap-output ios/Assets/Pegasus.js.map --assets-dest ios/Assets"
}
</code> <code># Run
npm run bundle-ios
</code>By packaging the RN module as a native library, developers can integrate Pegasus like any other native dependency, without altering their existing toolchain or dealing with JS tooling.
Communication Issues
Two main communication directions need handling:
JS ↔ native.
Module ↔ host app.
JS → Native
Implemented via
NativeModulesand
RCTBridgeModule. Example:
<code>@interface PEGDataProviderModule : NSObject <RCTBridgeModule>
@end
@implementation PEGDataProviderModule
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(getUsername:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
if (resolve) { resolve(@"Jack"); }
}
@end
</code>JavaScript side:
<code>import { NativeModules } from 'react-native';
NativeModules.PEGDataProviderModule.getUsername().then(username => console.log(username));
</code>Native UI components can also be exposed via
RCTViewManager, allowing JS to render custom native views.
Native → JS
Native code can send events using the event emitter or invoke JS by creating an
RCTRootViewwith a module name and initial props:
<code>RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"ImageBrowserApp"
initialProperties:props];
</code>Pegasus heavily uses this pattern to launch RN components from native code.
JS Page → Native Page
By exposing a router as a bridge module, JS can trigger native navigation via a URL scheme:
<code>@implementation PEGRouterModule
RCT_EXPORT_METHOD(route:(NSURL *)url) {
[self.router routeURL:url];
}
@end
</code>This enables seamless transitions between RN screens and native screens while preserving native navigation animations.
Conclusion
The article presented the common JS‑native communication methods that make Pegasus possible, and showed how packaging the RN module as a native library lets existing apps adopt RN capabilities without touching any JavaScript code. Upcoming topics will cover dependency management, development/debugging workflows, and dynamic deployment of RN modules.
Baixing.com Technical Team
A collection of the Baixing.com tech team's insights and learnings, featuring one weekly technical article worth following.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.