We’ve been using React Native in production for a few months. You might be considering React Native for your iOS app, or just casually wondering why we would do such a crazy thing, so here’s the story.

The BLUF is that React Native helped us ship crucial features in less time and immediately increased the percentage of our team that contributes iOS code. It’s working out, and we’re only going to increase our investment.

How did we get there?

Pre-History

The app is about a year old; we originally hacked it together with a web-native hybrid interface, and incrementally added native components over time. The web view code is written mainly with React and Flux, which was already a boon to our productivity and reduced bugs.

Fast-forward to a few months ago: we needed to build important features for our registration experience in a short amount of time, and they had to be done natively. Registration has available immediately and make a great first impression, so implementing these features in a web view wasn’t an option.

Since it was native, it fell on me (the lone native iOS engineer) to make it happen. Now, I’m pretty competent with UIKit, but I knew that “lots of new things” plus “not a lot of time” plus “critical” plus “iOS” ends in sadness. So I looked for a short-cut.

Hacking

Web development used to feel just as daunting under similar circumstances. Then React came out, I adopted it at Propeller, and the work didn’t feel so intractable. I learned the benefits of isolated components, explicit state, and unidirectional data-flow, but I wasn’t getting that out of UIKit or the iOS ecosystem. Out of options, I hacked together an integration of React Native into our app to see if it would do the job.

I started not with the critical new features, but by re-writing a small isolated feature into React Native. It took less than a day, I learned the machinery of integrating it with our codebase, and hey — I happened to make some part of the app feel noticeably smoother.

After getting my feet wet, the new features came easily. Smarter folks have written about the joys of React and Flux, and I found they still applied to React Native. We got the work out on time and into the hands of our users.

Benefits

This crazy thing worked, we got our features shipped, and I haven’t seen any React Native-specific crash reports in the wild. What happened next?

Sharing

I mentioned earlier that we already used web views in our app, powered by React. While we don’t have any “universal” components, we do have some tooling and utility methods used by both sets of JavaScript code bases. But this is also a double-edged sword: it’s not immediately obvious that the web view JavaScript exists in a separate world than the React Native JavaScript.

Testing

Automation and iOS have never played nicely: you traditionally need an OS X build node, which probably makes it a special snowflake in your build ecosystem. But since React Native code uses JavaScript, we don’t actually need to compile any Objective-C to execute portions of our code.

Good chunks of the app’s business logic is totally unaware of the iOS environment and be unit tested under Node. We can use any old Linux build server, tests run much faster, and are ultimately more reliable.

Team

If you know React on the web, you can start contributing to a React Native codebase with very little friction (especially relative to learning normal iOS development). There are still some bumps compared to writing web apps, especially around debugging, but everyone has contributed some kind of bug fix or feature.

Lessons

Not everything has been roses. Here are some mistakes that we (and by we, mostly I) made when first integrating React Native, and hopefully you can learn from them.

Navigation

Like many iOS engineers, I am cursed with enough experience with UIKit to do some really dumb things. One aspect of our app is a navigation bar with custom UINavigationBar hackery. It looks great — except neither Navigation nor NavigatorIOS supported our customizations.

Instead, I decided to be just so clever and wrap each React View in its own custom UIViewController subclass. We got to use our custom navigation bar, we kept the native interactions and transitions — but it added a whole lot of bouncing between Objective-C and JavaScript. This stuff is hard to debug if you didn’t write it. We’ll be switching to Navigator soon.

Many Worlds

Introducing React Native increased the mental overhead of answering, “Wait, how does this app work?” There are now three distinct user-interface environments in our app: web views, React Native views, and UIKit views.

Unless you started from scratch with React Native, you’ll have this problem. Crossing data between all the combinations of boundaries is possible, but we try not to do it often because it feels brittle.

Pre-1.0

This might go without saying, but React Native hasn’t hit a stable release and its APIs are evolving. Our app had some not-so-trivial Objective-C glue to get React Native working with our existing code, which stone-walled our attempts to upgrade for awhile. It’s incredibly exciting to see the regular release cadence of React Native, and one day we’ll get caught up.