Going Clear: React Native and the Prison of Cross-platform

07 januari 2019

Oscar Nylander

Here at Hedvig React Native is a cornerstone of our frontend, given that our mobile application is the primary means of delivering our service (Home Insurance). One of the values our company holds dear is Remarkable Experience: we actively strive towards providing all of our members with a truly Remarkable Experience in insurance, an industry that traditionally has been filled with truly unremarkable or even downright frustrating experiences. React Native has steadily been increasing in popularity, most likely piggybacking on the success of React Web. We at Hedvig are big fans of React Web and the style of programming it brings: we are using React for all things web and have no particular plans of leaving it behind. React Native is, however, unfortunately a different story.

The usual arguments for using React Native

JavaScript

There is undeniably a lot of JavaScript developers on the market due to the web
being one of the primary ways we deliver software to end-users. Being able to
tap into this pool of developers is a lucrative prospect indeed.

Native elements

This factor is lucrative for anyone who has been working anywhere near any
project attempting to create mobile applications using cross-platform frameworks
based on Web technologies, such as Cordova. It's well known that these
applications tend to 'feel' poor, owing to the fact that they are just simply
web pages loosely encapsulated in a thin application shell.

Cross-platform

One code base for two platforms? Sounds a lot like there's money to be saved
here, right? 🤑

Shorter development cycles

Again, we only need one codebase! 🤑

Continuous Delivery potential

With JavaScript, you can just push out a new bundle and your app is now suddenly
right in the hands of all of your users. Instant delivery has been great on Web,
right? Business people and developers love it alike.

Better developer experience

Hot Reloading? Say no more! After all, this is why doing web dev is such a nice
experience these days, no?

Why React Native falls short, in our view

Is JavaScript even a benefit?

You will be paying a performance hit for this. In many cases, this does not
matter, but it can, just like in the case of web-based mobile applications,
translate to a poor experience in the form of various stutters, dropped frames and
ruined animations. This may not be a dealbreaker for you, but if you are
interested in delivering a remarkable experience, it very well might.
There is also always the issue of the Native bridge, which is the glue that
connects your JavaScript to the world of Native code. Merely using this bridge
comes with a significant performance cost, and there's also the architectural
issue that this bridge is entirely asynchronous (everything is a promise,
period). The Fabric re-architecture will hopefully do a little bit to remedy
this issue - fingers crossed.
The Native bridge also imposes the very significant limitation that you cannot
in a practical manner animate any layout property you'd like. Instead, you're
stuck with the Animated-API with useNativeDriver. If you're not
doing anything to the slim subset of properties Animateds native drivers can
manage you're going to be stuck performing the animation from JavaScript which
is, as you can imagine, dreadfully slow (ie unfit for animations which need to
run at 60 fps).

Filled to the brim with obscure bugs

We use Sentry to track issues that may occur in our application in order to make
sure that the experience we provide to our user is smooth and issue-free. Allow
me to share a typical Sentry issue that we would get from our React
Native-application:
EXC_BAD_ACCESS
hash > isEqual: > moduleMapping > stringWithFormat: >
Attempted to dereference garbage pointer 0x20a800000000.
The issues are not particularly easy to decipher and do require a fair bit of
digging into the internals of React Native. I've never had to spend so much time
code browsing until I started using React Native.

Libraries are of generally poor quality

Possibly owing to the fact that React Native is relatively young, possibly owing
to the large share of mostly-JavaScript only developers, the available libraries
in React Native are generally of pretty poor quality. This has lead us to have
to fork a large number of libraries. The benefit of libraries becomes kind of
negated if you're constantly forced to fork the libraries, learn the code and
finally fix the issues present in the code. It is, of course, nice to be able to
contribute back to the open source community, something we're big believers in,
but when more time is spent in making the libraries work than creating a
remarkable experience for our users, something is simply wrong.

Developer experience isn't even really any better

React Native touts an improved developer experience, with all the familiar
techniques from web development, such as Hot Reloading. Hot Reloading is a
fantastic feature, BUT, on React Native it really does not work as good as you
would hope. I find myself being frequently forced to reload the entire
application anyways (the classic Cmd-R on iOS or R-R on Android).

Continuous Delivery for apps is a hassle

Continuous Delivery for apps is something that seems extremely lucrative when
coming from the background of Web development, and it is undeniably a cool
feature. Missed a bug? You can just push out an update and your users won't even
have to update their application!
This only works until you want to update any of your native dependencies, in
which case you lose the ability to push out new javascript bundles, as your
bundles must always be compatible with whatever native code happens to be
running in your application. Any upgrade to these means you're out of luck.
There's also the issue of code bundle updates being a little bit iffy when it
comes to the App Store T&C's, in that you're technically not allowed to provide
new functionality via code bundle updates, as Apple requires an App Store review
whenever new functionality is introduced.
Combine this with the fact that a large number of users have automatic updates
enabled for their App Store/Play Store and this is largely a subject which we've
found is not worthy of our time.

The curse of the lowest common denominator

It should be noted that the users of the different OSes (we support iOS and
Android) have different expectations for how applications in their OS are
supposed to behave. If you care about providing a good user experience this must
be a part of your design process. React Native does not provide these things
automatically - that would probably be impossible - and hence you will need to
spend a large amount of time making sure that things behave properly in each
platform.
As this is generally a huge hassle, React Native applications have a tendency
to move towards the lowest common denominator in user interfaces, leaving your
application feeling less like a native application and more like what a
cross-platform solution like Cordova feels like. Not quite as bad, but still not
quite as good as your app could be.

This is what we've decided to do in response to these experiences:

Go full native

As we are very concerned with being able to provide the best possible experience
to our users, we've decided to transition away from React Native. We will
instead build all our new features fully natively. It was not a decision we made
lightly - we are still a startup with limited resources - but we've reached the
conclusion that we simply cannot deliver a truly remarkable experience if we
stay with React Native. We also think that the time saved from being
cross-platform is overstated given the large amount of time we spend trying to
fight React Native and its ecosystem to actually deliver the things we want to
be able to deliver to our users.

Get there via brownfield

Luckily, the navigation library that we use, react-native-navigation, provides
us with an escape hatch. We can use the registerExternalComponent-method to
gradually introduce fully native components into our applications, while not
being forced to re-write the entire application for both platforms right away.

For what kind of projects would React Native be a good fit?

Bootstrapping startup –
When you're in the very first stage of a startup and you've decided that you
will deliver your service through a mobile application (make sure that this is
actually the right thing to do first! It likely is not), React Native might help
you shave off a few days of development that may just prove to be invaluable.
React Native may be a good candidate here - just make sure that you get off the
train in time until you've already built yourself into a corner.

You want the app-equivalent of a WordPress web page –
If you want to shave off some of the cost of development you may want to
choose React Native to build your application. Just please make sure that you
understand that you will be paying the price later on in maintenance costs and
it will definitely be at the expense of your users' experience with using your
application. If you do not care about this, you may want to use React Native.
Then again, if this is the case, why are you even building an application?
Websites are way cheaper and can probably do whatever you need. As mentioned in
the previous section, make sure that you actually are willing to create a mobile
application - these things, like all software, cost a lot of money to make.