At Oak City Labs, potential clients often ask if we write apps using React Native. Why not? Isn’t that the fastest way to market — write one app for both iOS and Android? That’s the crux of React Native’s pitch. Don’t spend time writing two apps when you can write a single React Native app instead. As CTO of a mobile dev shop, I should be able to answer that, so I’ve started doing some research on what it takes to be a React Native developer. How does React Native compare to Swift/Java development in terms of efficiency, stability and maintainability? I’ll walk through some of the things I’ve found.

Just a note before we dive in: this article addresses the idea of writing apps for iOS and Android in JavaScript, focusing on Facebook’s React Native implementation, not to be confused with the reactive programming model, which may be a compelling alternative to the traditional declarative programming style.  Reactive programming is a topic definitely worth following.  

Toolboxes

How many tools does a developer need to build an app? As an iOS developer, I live in Xcode, provided by Apple and designed to build apps for the Apple platforms. Well over 95% of my development time is spent in Xcode. Apple also provides Instruments, a suite of testing tools to examine memory, CPU, etc in your app. Occasionally, I use Instruments to track down a particularly difficult bug. That’s it — Xcode and a little bit of Instruments.

The situation for Android is even a little easier. Android developers use Android Studio, a tool provided by Google with the sole purpose of creating Android Apps. Features like memory analysis and CPU monitoring are built in, so there’s really just one hammer in the toolbox. Android developers live in Android Studio.

Now onto React Native. It’s hard to find data on the amount of shared code in React Native apps, but conversations like this one suggest it can be 80% or 90%. That’s still a significant amount of platform specific code. Let’s assume we’re building an average app that has at least 15% platform specific code.

React Native developers have a bigger hill to climb just to get started. According to their instructions, here’s the list of software to install to build a cross platform app.

  • Homebrew — A package manager that makes it easier open source tools on your Mac
  • Watchman — A utility from Facebook to watch the filesystem for changes and run commands in response to those changes
  • Node — A javascript runtime built on Chrome’s V8 JavaScript engine, often used for server-side JavaScript
  • NPM — Part of Node, this is another package manager for managing JavaScript components
  • react-native-cli — Command line interface for for interacting with the React Native environment
  • Xcode — Necessary for various iOS tools
  • Android Studio — Necessary for various Android tools

In this article, Tony Mann suggests you’ll need these other tools as well.

  • Flow — A static type checker for JavaScript
  • Chrome Debugger — Chrome’s JavaScript debugger which can attach to your React Native application
  • Babel — A JavaScript to Javascript compiler

Now you’ve got everything you need to build a React Native app… except a text editor, so find one of those too.

So here’s the rundown for setup requirements

Platform Number of Tools Required
iOS 2
Android 1
React Native 10+

 

Right out of the gate, the bar is set relatively high for a React Native developer to get up and running. If this were a one-time penalty, it would be easy to write off. One day lost to setup on a six-month project isn’t significant, but this represents a whole dependency tree. Any update in one component can have a cascade effect that forces upgrading other components. Maintaining this whole setup now becomes overhead that the React Native developer must deal with. This kind of yak shaving can regularly consume a day of developer time.

Writing Code

I’m an iOS developer, so I’ll address the writing of code as a discussion of Swift vs JavaScript. For the sake of brevity, let’s assume Java (or Kotlin) developers make similar arguments.

JavaScript is not a nice language. It’s stone aged tech compared to Swift. Ariel Elkin does a fantastic job in this article walking through the many technical shortfalls of the language. Some of the highlights include

  • Weak typing
  • Lack of optionals
  • Lack of functions signatures

Issues like weak typing and lack of optionals are specific issues from Objective-C that Swift was designed to solve. In my experience, we always struggled with nil pointer exceptions in Objective-C. A rogue nil was the root cause of the vast majority of crashes in our applications. These have all but disappeared with Swift. A whole class of very common crashes has been fixed by using a language that simply doesn’t allow it. The rare nil pointer crash now usually has to do with interacting with legacy Objective-C.

Strong typing, optionals, and other features of Swift let me quickly write expressive, memory safe code that won’t crash. The compiler makes sure of that. As Elkin points out, these crashes happen frequently with React Native. JavaScript for app development is a step (or leap?) backward technically. If our goal is efficient developers, we should empower them with the best tools available.

Testing is an integral part of our app development process at Oak City Labs. One of the best ways to encourage developers to embrace testing is making it as painless as possible. With React Native, developers get another stack of dependencies to maintain just to get the unit testing framework running. In this article, Carlo Francisco goes over the testing stack they use at Refinery29 to unit test their React Native code. It’s based on Jest and Calabash / Cucumber. Jest is a JavaScript unit testing frame. Calabash and Cucumber are used together for application level acceptance testing. Calabash and Cucumber (and any customizations) are written in Ruby. The actual Cucumber tests cases are written in another language called Gherkin, one of those terrible languages for non-developers which are still too difficult for non-developers and too weird and restrictive for developers.

It’s great that there are testing mechanisms for React Native, but in order to accomplish real testing, we’ve got to add another few rooms on to the house of cards we’ve built so far. Not only do we add more 3rd party JavaScript frameworks, but we can also tack on extra languages — Ruby and Gherkin — in order to implement application level testing.

Compare this to iOS development in Xcode, which provides XCTest for unit testing and XCUITest for application testing, all written in Swift. Likewise, Android developers have JUnit and Espresso for unit testing and application testing respectively.

Stability is definitely a casualty here, mostly because of the limitations of JavaScript. React Native also loses ground on maintainability as testing tools require more third party components be placed in our growing dependency tree. I worry about efficiency too since testing now requires a React Native developer to know even more languages.

Debugging Code

Finally, once the code is written and running, there’s always debugging to do. According to React Native’s documentation, there’s no one stop shop for React Native debugging. There’s an in-app developer menu that opens the door to turn on/off some debug features and provide an onboard inspector. Using the Chrome browser’s remote debug feature seems to be the most powerful way to connect to the React Native app and view internals. There’s also a standalone version of the React Dev Tools to use when you can’t connect with Chrome’s debugger. And finally, there are the native debuggers in Xcode and Android Studio when you need to debug pieces of native code.

Debugging apps written in the native language is much more straightforward. To debug a Swift app in iOS, run it from Xcode and debug. For an Android app, run it from the Android studio and debug. It’s such an integrated part of the development cycle with the native tools, it’s easy to take it for granted.

With no one definitive debugging environment, I worry about a React Native developer’s ability to efficiently track down a bug. I assume one would start debugging in one of the JavaScript console tools, but then you might have to jump to a native tool. As context switching goes up, efficiency goes down.

Tool Quality

I’d also like to comment on the tool quality. Much of React Native’s tool chain, react-native, npm, etc, is executed at the command line. While some developers will praise the hard core grit of the command line, (“Real developers type, not click!!”), I find that it increases the entry-level barrier for new developers and generally causes friction for developers at any level. Trying to remember the flags for subcommands of the react command line tool isn’t going to help ship an app faster. Compare that to a button or menu item in a more robust tool like Xcode or Android Studio. The cognitive load added by a bunch of command line tools is just another stone weighing down the React Native developer and causing efficiency to sink.

Adding It All Up

At the end of the day, the React Native developer needs quite a big toolbox to fit all their tools in. Here’s the list:

  • Homebrew
  • Node
  • Watchman
  • NPM
  • react-native-cli
  • Flow
  • Chrome-Debugger
  • Babel
  • Xcode
  • Android Studio
  • Standalone React Dev Tools
  • Jest
  • Calabash
  • Cucumber

In order to use all these effectively, the React Native developer also needs to have a working knowledge of these programming languages:

  • JavaScript
  • Swift/Objective-C (iOS native components)
  • Kotlin/Java (Android native components)
  • Ruby (Cucumber testing)
  • Gherkin (Cucumber testing)

The single platform iOS developer needs Xcode (and maybe Instruments) to write, test and debug applications in Swift. Likewise, the Android developer needs Android Studio to write, test and debug apps in Java and/or Kotlin.

For a shop that has experienced Swift/Java developers, it’s very clear to me that there is zero reason to switch to the React Native development stack. We’re concerned about efficiency, stability and maintainability. The enormous number of tools required for React Native along with the piecemeal nature of programming environment are going to tank the efficiency of an React Native developer. Even the world’s best JavaScript developer is going to face an uphill battle on this unlevel playing field. JavaScript as a language is the biggest barrier to stability. JavaScript, by itself, is a deal breaker for us. Maintainability is another worry with so many dependencies from so many sources and keeping it all playing well together. (Not to mention dependency on Facebook’s ongoing support after the Parse.com incident.)

I believe in using the right tool for the right job. Writing mobile apps in Swift/Java is the quickest, most friction free path to shipping apps to customers. I can understand how React Native appeals to web developers, offering to turn their JavaScript experience into mobile apps, but there is no free lunch. It may work in the end, but JavaScript (plus a lot of frameworks) can’t match apps written in the native toolsets when it comes to quickly and efficiently shipping a high quality, maintainable native app.