Converting an app to React Native — Why and how

This post is part 1 of a series, and we will be posting more in the coming weeks.

Tracking progress

Imagine

With the Imagine app we aspire to help people with skin diseases track their conditions and treatment. You simply take a picture of your lesion at a fixed interval, and see if you’re improving or getting worse. Some of the most common skin conditions like psoriasis have no cure, meaning people have to find ways of coping with it. The Imagine app helps people track and understand their condition.

Why React Native?

We originally built Imagine for Android and iOS, respectively with Java and Swift. While we were happy about the actual apps, it started to become a hassle to add new features, because we needed to make them twice. We also noticed that we had slight differences in analytics, which is a pain when we try to make decisions based on data. We’d been keeping a close eye on cross-platform initiatives like Xamarin, Ionic, and React Native, and decided to look into React Native as a possible replacement for double native code.

We decided that Xamarin or Ionic would not be a good fit for our project. Xamarin was not a great option for us because we wanted to share UI code. Ionic simply did not meet our requirements when it came to performance, being a WebView-based framework. So we looked more into React Native.

The promise of React Native is that we can save on development time, especially for features that are mostly UI-based. Our initial experiments definitely showed that to be true. We had some parts that would have to stay native, like accessing the camera and using the GPU to process images, so we were happy to learn that we could re-use existing code as part of native modules or native UI components in React Native. Being able to share the analytics and business logic between the platforms would help us ensure consistency across platforms. We were a bit worried that React Native still has an immature ecosystem, and it gets breaking updates frequently. In the end, though, we decided that the pros outweigh the cons, and started the process of converting the app to React Native!

Approach

There are two possible approaches to converting an existing app to React Native:

  • Add new features in React Native and replace existing features piece by piece
  • Completely rewrite the app from the bottom up in React Native

In order to get the most out of the resulting app, we decided to go all in and start from scratch. That way we could set up the project the React way and go for proper idiomatic solutions all over. (We will cover our setup in a future post).

Taking photos

One worry for us was that we had a few features in the app that can’t be achieved with just JavaScript. One example is our use of the camera. We don’t just want to take pictures, we want to take the best possible pictures of skin, and to that end use things like computer vision and custom shaders. There are some libraries out there to bridge the camera functionality, but because of our very specific setup, we wanted full control over that part of the app. We looked into native modules and native UI components in React Native, experimented a bit, and decided that we could make it work. (We will cover native modules and native UI components in a future post as well).

Journey

We were eager to get started, but we realized early on that it’s hard for a (small) team of native mobile developers to jump into JavaScript development, especially with no knowledge of React or Redux. JavaScript and React Native take some getting used to, and we often ran into (seemingly) simple issues where we were used to having straightforward solutions, but suddenly we had to do some research to get to a good solution. Luckily enough, we soon got some people on board with the necessary knowledge to set up a proper backbone for a JavaScript project of this size, which sped things up significantly!

That’s one important lesson: Developers with React experience will feel at home and can start contributing to a React Native project immediately. The framework, the language, and the tools are all very web developer friendly. Flexbox styling also feels fairly familiar if you are coming from the web. The native developers, on the other hand, needed much more time to get used to an entirely new setup for writing code and running unit tests, and wrap their heads around a brand new app architecture.

The lack of strong types was also the cause of many headaches at first, but eventually we set up the proper tooling to deal with this (and this will be covered in a future blog post).

We really enjoy the quick feedback loop for building UIs using hot reloading, and that we can test those UIs on iOS and Android at the same time. That said, the development environment seems to break more easily than you would expect. We had some annoying issues with Node and NPM, where it was necessary to reset caches or even reinstall tools to get things to work again.

Result

Pros

A lot of people claim that cross-platform apps are slower than pure native apps. Obviously there is a small delay in React Native because it still is a layer on top of the OS, but in the end, we found that to be negligible. The app does feel slow for development builds, but once we create a non-development package it feels just as fast as a pure native app. The documentation actually contains a nice write up on performance.

Tracking different body areas

Having a single code base for both platforms means that API communication, business logic, and UI components only need to be written once. This is a huge time gain for the team.

Cons

React Native does require us to sometimes make compromises on the layout, and where we don’t want to do that, we need to go for custom views. This can sometimes slow us down when we work on certain parts of the app.

The huge amount of third-party libraries that are used for JavaScript projects is also a bit of a turn off when you come from native and try to limit dependencies. Even if you don’t add any libraries yourself, React Native already includes more than 50 dependencies that you can’t opt out of. This is, however, how JavaScript projects work, and most of those dependencies are quite small and focus on just one thing.

Another thing is the pace at which React Native gets updated. If you skip a few versions, you might end up with a painful upgrade later on. React Native gets updates every month, so it can be hard to keep up sometimes.

Conclusion

In the end, we are quite happy with the result, and we’re looking forward to adding new features with much more ease.

In the next few posts, we will cover some technical aspects of the move to React Native, like which tools should be included in a proper React Native development setup and how to get started on native modules and native UI components.