Rob Fahrni

Follow @fahrni on Micro.blog.

Migrating from Native to React Native

Something we’ve become very good at, at WillowTree, is migrating native code to React Native incrementally. I’ve been working on a project for the last year-and-a-half to migrate a pretty big application to React Native and we’re not there yet. We’ve finally hit critical mass in the React Native codebase to begin the transition to a full React Native based application.

To start the migration we built bridges to the native iOS and Android applications to handle things like; networking, navigation, analytics, telemetry, and events.

Duct Tape, fixer of all things!Since the native applications had great code written for all of those things we were able to leverage it to build a brand new user interface using React Native. At one point we had a Platform Team who built out the Bridges between React Native and Native. Those teams consisted of two native iOS and Android developers and a React Native team of two. The two React Native developers built up the TypeScript side of things for communication between React Native and Native, leveraging our native bridges.

To make writing new React Native UI easier we had individual teams working on specific parts of the UI by feature. A mono-repo was created so the team could use Expo to quickly iterate the parts of their feature. The React Native Platform team made sure the App Team could mock everything and use the bridges as they’d be used with the native application. It worked out really well.

Since each feature area had its own team we would take their code and make a package out of it. Those NPM packages were then referenced and used in the native iOS and Android applications.

Let’s say we had a Social feature. That feature team would do everything to make that feature work as expected, complete with UI, business logic, and any navigation necessary to complete the feature. That feature would be bundled up into a package and deployed to a private NPM repository.

The native application would reference that package in its packages.json file by its version number and it would be installed using NPM. Once we had it packaged into the native application we would reference it using the supplied Meta React Native library code in each native application. Once the React Native Framework was strapped up it was easy to wrap up each React Native feature and display its UI, where it was completely controlled by the React Native developers.

Hybrid React Native / Native Application

Networking and Models

To supply the React Native code with models ready for consumption we bridged to the native applications since each had already implemented network and data caching code. The React Native bridge allowed the caller to supply all the arguments necessary to make a network request and the bridge code bundled it up, sent the request, received data (or POSTed it), updated any local caches, and forwarded it to the React Native code where it would become a TypeScript model object ready for them to consume. It was some of the first code we created and really opened the door to productivity right away.

I’m going to talk about this from an iOS perspective, since it’s what I know.

This was a bit tricky. Navigation from React Native to Native isn’t a straight line. When creating a React Native feature we first constructed an RCTRootView. This would ask the React Native framework to load a particular feature and host it inside the RCTRootView. Once the React Native code was loaded it was running in its own little sandbox, so to speak. It didn’t have any knowledge of the outside world, except through our native bridging code.

Our navigation bridge was quite simple. It was a single method that allowed code to navigate to a native UI View Controller or to a React Native View Controller based on the name of the UI the developer wanted to navigate to. Yes, the bridging code had to know about every view controller in the application, to an extent. The developers of the iOS and Android code thought ahead and created a really nice set of routing classes. Those were leveraged to display most view controllers, native or react native.

While each feature was self contained this navigation bridge was there to provide the glue that allowed us to display anything we wanted.

One bit that felt weird was the need for the React Native code to know when it had to close itself. The native code built the UI and was hands off at that point but we had to know when to tear down that UI. The React Native code would tell us when it had to go away. Unbalanced, but a compromise that worked beautifully.

Other Stuff

Brain in a jarThere are a few more bridges we built. Each defined in a separate package supplied by our private NPM repository and built into the native applications. I’m not going to get into those now. This post was kind of off the cuff and I could go really deep into the architecture, but that is for another day, if at all. 😄

I will say this. If you were not aware you were looking at a hybrid React Native / Native application you wouldn’t know. Since React Native uses native controls for each platform you are truly getting a native application. Sure it’s using the JavaScript engine from the platform for writing code and sure the UI is described in web technologies, but the Meta React Native framework does all the work to display 100% native UI controls. That’s what makes it difficult to tell the difference.