How we built Bolt scooter service app with React Native

Rasmus Lelumees, Senior Software Engineer at Bolt

Home (map) and QR scanner screenshots of Bolt scooter service app — taken from the iOS build

React Native has gained notable traction during the past years and is currently featuring over 75k stars in GitHub. Despite the popularity, it has also received some justified criticism (e.g. from AirBnB) due to immaturity and inconsistencies.

Is this fast-moving platform finally ready for production-grade apps?

We decided to give it a go. Here’s how it went.

Background

We needed a new app for collecting and deploying Bolt scooters to support local operations in Paris. It was not too complex but had some obvious native dependencies. The main screen featured a map view which required location access. In addition, we needed the camera to allow scanning QR codes. Since our mobile teams were busy enough, we took a couple of web developers with React expertise and asked them to build the app for iOS, Android and web. Luckily, they agreed.

Disclaimer: we did not dive into the deepest part of the ocean with this initiative, as the new app would be for internal use only (at least for some time). Saying this — if this initiative proved to be a success, we would definitely consider React Native for the future public apps as well. If not, then fewer users would suffer from this failure.

What about other cross-platform frameworks?

Flutter, Ionic, Cordova and NativeScript are tough competitors for React Native, but our core reasons for choosing RN were:

  1. Previous React (and JS) expertise
  2. Community size and popularity
  3. Native feel and performance

In addition, it looked like we could use react-native-web to share the native codebase with the web app — a huge bonus given our tight time frame.

So how did it go?

In a nutshell, the project was a success. The web-based MVP was launched in 4 weeks and iOS followed another 4 weeks later. Android was left last and required an additional week to get it almost 100% working. This was a conscious decision — configuration-wise the complexity (some gut-feeling included here) looked like: Web < iOS < Android.

In 2 months we did a Beta-release which already included a redesign and more features like the camera-dependent QR scanner. But despite the great speed we definitely hit the wall more than once.

Any drawbacks? Yes.

Diving into native

React Native does a lot to minimise the amount of native code you have to write. In fact, for simpler apps, the need for native additions are microscopic or non-existent. However, if you want to dig a bit deeper, customise configurations or just debug some dependencies to the core you will need to get your hands dirty by adding some lines to Java (and Gradle) or write some Objective-C. Bridging native platforms is hard and we did not expect everything to work out of the box, but getting to full speed took us a bit more time than we anticipated.

Quality of third-party packages

In short, the average quality of RN third-party packages is worse than in the web ecosystem. Often it seems like someone with deeper knowledge of one platform has written the problematic package and it works great on that one platform but sucks on the other. In our case, it hit us badly because we did not move in parallel with all the platforms. These problems were unexpected and sometimes it took us days to work around it.

Bugs

Despite being around for a while now, React Native still has some bugs. I guess there is no platform that doesn’t, but with React Native there were some which were pretty critical and still open despite being reported a year ago (or even more). Less critical (but sometimes really annoying ones — like Android dimensions being broken or faulty exception stack traces) were closed as being stale or without any reason at all.

Other

At some point, you will definitely wander in GitHub issue threads and PR-s to figure out if the cryptic error message you just saw was caused by your code, some problematic package or React Native itself.

Once you realise the problem is not in your code, you will spend many hours contemplating on if and when there is a fix coming up. Perhaps you will even submit issues and PR-s to speed things up a bit. That’s great if you want to contribute to open-source, but not so great when you’re in a bit of a hurry with your project.

Spice it all up with leaky abstraction, weird crashes and other interesting problems like Android relying on an ancient JSC (which hopefully will be solved in 0.59) and you will realise pretty quickly that the talks about immaturity are indeed justified. Bigger engineering teams can afford to develop their own forks and bridges to patch these issues, but for smaller teams, this won’t be a likely option so you just have to accept these nuisances.

On with the good stuff

Shared codebase

Some teams claim that they have managed to achieve over 90% shared code using React Native. In our case, it was more like 80% of UI code and 100% of business logic. In total it averaged to slightly below 90% of shared code which we think is pretty great.

Speed

Despite having numerous setbacks, we still kept up the pace and released in time. We considered it a success given the lack of native (and React Native) experience. Much of this comes from great tooling, but not only. The community of React Native is active and helpful plus the quality of tutorials and documentation is increasing which in turn improves learnability.

JS tooling

React Native allows using a familiar tooling for web developers which helped flatten the learning curve for us. For example, you can use the Chrome developer tools and take advantage of React or Redux toolset meant for web apps or even use Jest for snapshot testing. Enabling hot reload significantly speeds up the development and reduces the build time to a few seconds which is far out of reach for native builds (sometimes taking many long minutes to build). JavaScript lacking type safety was a concern for some until Babel 7 shipped with integrated TypeScript support.

React

We really enjoy(ed) using React. It is declarative, simple and scales well. Downward data binding helps to prevent errors and mess in the code and components with a well-defined lifecycle enforce separation of concerns.

Yoga layout

React Native features Yoga cross-platform layout engine which implements Flexbox — a layout module well-known by its efficiency in the web ecosystem. It was a joy to see flex-like behaviour in the native platforms as well.

To sum it all up

We believe that React Native will mature even more with the help and support from both the community and Facebook engineering team. Despite all the issues it already felt like a really promising piece of technology. Bear in mind that it has its flaws, but is also a joy to work with.

We met our goals and built an app that actually felt native on both iOS and Android. Thus we already know that React Native will find usage in our future projects. Next time in public production apps with heavier load already.

What about your experience with React Native? Let us know in the comments below!

Also, if this blog post got you excited about what we’re doing, remember that we’re hiring — check out our careers page.