Integrating React Native into a Legacy Mobile App: Baixing’s Pegasus Journey
This article details Baixing.com’s exploration of React Native for app dynamism, covering their initial RN experiment, the challenges faced, and the design of the Pegasus module that embeds RN components into native view controllers for flexible, high‑performance mobile updates.
React Native and app dynamization have long been hot topics in the industry. The Baixing mobile technology team has actively explored these topics and accumulated valuable experience. They plan to publish a three‑part series to discuss their findings.
The series will cover:
Baixing’s React Native journey;
Integrating RN modules into existing native projects;
JavaScript‑native communication in RN modules;
Solutions for external dependencies of RN modules;
Development and debugging of RN modules;
Dynamic deployment of RN module code.
Background
Baixing’s app lets users search, browse, and post content on mobile devices. Like most API‑driven apps, it fetches data from the server and renders UI, essentially acting as a native browser. However, native apps face challenges in real‑time code deployment due to build, runtime, and app‑store review constraints.
To respond quickly to changing requirements, the team tried several app‑dynamic solutions:
Pre‑install native components locally and render UI based on remote configuration;
Define URL‑based navigation for core business pages;
Use WebView and JavaScript bridge for a hybrid approach;
Adopt hot‑fix frameworks like JSPatch and Tinker.
These measures alleviated some needs but still had limitations: configuration‑only pages, performance bottlenecks in hybrid solutions, and unsuitable hot‑fix frameworks for heavy business logic.
Seeking a framework that supports both business development and dynamic deployment with good runtime performance, the team identified React Native (RN) as a promising choice. RN offers several strong features:
Develop native mobile apps using JavaScript, the most popular language;
Leverage React’s “Learn Once, Write Anywhere” philosophy for a web‑like development experience;
Cross‑platform code sharing to boost development efficiency;
Runs on JavaScriptCore without compilation, enabling dynamic deployment.
Baixing’s React Native Journey
Baixing Business
Initially, the team avoided using RN directly in the main app due to technical risk and instead built a separate merchant‑side app called “Baixing Business” for VIP users. This app adopted RN’s recommended development pattern, starting with the command: react-native init AwesomeProject Except for a native messaging module, all features were built with RN. However, they encountered several issues:
Community components often lacked professional quality, containing bugs and performance concerns;
Integrating third‑party plugins via react-native link was problematic compared to CocoaPods or Gradle;
RN hides UIViewController and Activity concepts, leading to view hierarchy and lifecycle issues in complex scenarios;
Lack of reliable transition solutions (see https://github.com/artsy/emission/issues/501);
Extensive native components and modules were still needed, increasing cost.
After this experiment, the team decided to adjust their approach when integrating RN into the main app.
Pegasus
Pegasus is the RN module embedded in Baixing’s main app. Unlike the earlier pure RN app, Pegasus is packaged as a native library. In iOS, the main app can be viewed as a series of UIViewController objects, each potentially embedding an RN component.
Pegasus provides various UIViewController subclasses, each containing a JS‑written component. When navigation to an RN page is needed, the corresponding view controller is simply pushed.
Creating a view controller that embeds an RN component requires three parameters:
A globally shared RN bridge instance;
The component name registered in AppRegistry;
Initial props for the component.
Example iOS code to launch a component named Profile with an initial name prop:
PEGComponentViewController *viewController; // limited line break for layout
viewController = [PEGComponentViewController alloc] initWithPegasus:[Pegasus sharedInstance]
moduleName:@"Profile"
initialProperties:@{ @"name" : "Jack" }];
[self.navigationController pushViewController:viewController animated:YES];The simplified interface is declared as:
// A generic RN component container
@interface PEGComponentViewController : UIViewController
@property (nonatomic, readonly) Pegasus *pegasus;
@property (nonatomic, readonly) NSString *moduleName;
@property (nonatomic, readonly) NSDictionary *initialProperties;
- (instancetype)initWithPegasus:(nullable Pegasus *)pegasus
moduleName:(NSString *)moduleName
initialProperties:(nullable NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;
@end
// Shared RN bridge manager
@interface Pegasus : NSObject
@property (nonatomic, readonly) RCTBridge *bridge;
@property (nonatomic, readonly) PegasusConfiguration *configuration;
+ (instancetype)sharedInstance;
+ (void)setSharedInstance:(nullable Pegasus *)instance;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfiguration:(nullable PegasusConfiguration *)configuration
router:(id<PEGRouter>)router
baixingAPI:(id<PEGBaixingAPI>)baixingAPI
dataProvider:(id<PEGDataProvider>)dataProvider NS_DESIGNATED_INITIALIZER;
@endKey points: pegasus holds the RN bridge and required modules; moduleName is the key registered in AppRegistry on the JS side; initialProperties provides the component’s initial props.
PEGComponentViewController uses RN’s RCTRootView to render the RN page:
@implementation PEGComponentViewController
- (void)viewDidLoad {
[super viewDidLoad];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:self.pegasus.bridge
moduleName:self.moduleName
initialProperties:self.initialProperties];
rootView.reactViewController = self;
[self.view addSubview:rootView];
// Other stuff
}
// Other methods
@endUnlike a typical RN app that uses a single global UIViewController and RCTRootView, this approach allows multiple view controllers each hosting a single RN page, offering clear module boundaries and native transition capabilities.
Implementation steps in a legacy native app:
Create a shared RN bridge instance at an appropriate time (e.g., after app launch) and load the JS bundle;
When an RN page is needed, instantiate the corresponding view controller or activity and navigate to it.
This strategy enabled the team to embed RN modules into a long‑standing native project, marking the first step toward dynamic RN‑based updates for Baixing’s main app.
Future articles will address common questions such as how Pegasus integrates with existing native projects, bidirectional communication, dependency handling, and code deployment.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
