Diving Into React Native
React Native looks like it provides a great set of tools to help mobile developers get a new app running on iOS and Android without needing to build the same app twice. Facebook and Instagram have successfully integrated React Native into apps used by millions, so the technology seems to be stable and ready to ship. I recently had an idea for a small app and thought I'd take React Native for a spin to see how it worked. Here's what I found.
(One note here...my goal was to implement both iOS and Android functionality, but I only tested the app on iOS. I assume the components that advertise Android functionality work without issue. I also have experience with JavaScript and React, so I didn't have any learning curve there.)
The app is fairly simple. It needs to take pictures with the phone's camera, save the photos, and allow the user to draw lines on the pictures. In OS terms, I needed camera access, file system access, and touch input with simple line drawing. Nothing overly complex.
Camera Access
Gaining camera access was fairly simple. React Native provides
abstrations for common UI elements, but it doesn't provide
camera access out of the box. Fortunately, the React Native
community has produced a lot of libraries to cover the missing
pieces. In this case, I found the react-native-camera
component, which offers access to native camera features on iOS
and Android. Integration was straight-forward and within a few
minutes I was taking pictures with my React Native app.
File System Access
Next up, I needed to save the pictures to the file system.
Again, I relied on the React Native community, which led me
to react-native-fs
. Within minutes I was able to
save my pictures to the app's document folder on iOS.
At this point, I probably had a couple of hours invested in my little app. It was working well on iOS, and I assume it would have worked just as well on Android. However...
Touch Input
Now I needed to respond to user input by capturing the path
of the user's touch input and drawing a line over the picture
in reponse. I know how I would have captured movement on a
web page, and I'm confident I could have figured out pretty
quickly how to capture movement in a native iOS app. It would
have been a huge learning curve on Android. So another community
component would have been ideal. Unfortunately, I wasn't able
to find such a component. I did track down a react-native-sketch
component, but it didn't provide Android support. At this point,
I could have continued on without Android support, but that was
my primary reason for considering React Native in the first place.
Just Native, No React
Instead of jumping into my own Android implementation for
react-native-sketch
, or continnuing to search for a
two-platform solution, I decided to see how the app might be different
if I implemented a native iOS solution.
I was able to implement a native iOS app in about the same
time it took to implement the React Native version (of course,
I no longer had an Android version of the app). However, I
noticed that using the native iOS APIs for the camera didn't
provide quite the same camera experience I had in the React Native
app. It turns out react-native-camera
is built on the
AVCapture*
APIs and I opted to use UIImagePickerController
in my native version of the app.
You can see in the screenshots that the iOS API I chose provides
a camera UI that looks exactly like the iOS Camera app. Complete
with access to flash and camera selection controls, it's a very
comfortable experience for iOS users. Since I had no past
experience with using any camera APIs on iOS, I didn't immediately
realize that the React Native implementation wasn't quite the same
"native" experience. It's likely I could have adapted the
react-native-camera
UI to mimic the native camera UI,
but I wasn't very motivated to travel down that path.
I also discovered that the native API provided a built-in screen to "Retake" or "Use Photo", whereas I had to implement this on my own with react-native-camera.
It's entirely possible that someone has implemented a React Native
camera component using the UIImagePickerController
API.
With some work, I could have done that myself, but if I'm going to
commit that time, and start building cross-platform components with
iOS and Android native code, I need to at least weigh that against
investing that time to simply build native Android and iOS versions
of the app.
To be fair, it's also possible to view the react-native-camera
implementation as more flexible since it doesn't lock you into the
iOS UI. So there are instances where that option may be preferable.
React Native Versions
I mentioned the lack of an Android implementation for the
react-native-sketch
component. However, while I was
searching for components, I also ran across community components
that weren't updated to the latest version of React Native. This
left me with the concern that adopting community components brings the
possibility that when updating to new versions of React Native, you
may need to first wait for all of your components to be updated (or
need to update them yourself). If you're adopting a lot of third
party code in your app, this may prove to be a significant problem
down the road. I don't expect this is a huge problem for popular
components, but it's something worth considering.
Of course, this is also a problem faced by developers when building iOS native apps with Swift. In the past, changes to the Swift language have meant waiting for udpates to third party frameworks before adopting new versions of Xcode and Swift. However, Apple's most recent releases of Xcode and Swift have addressed this by offering build tools that will compile older Swift syntax.
iOS Versions
In addition to handling React Native versions, what about iOS versions? Building with React Native potentially means that you will face delays taking advantage of any changes or additions to iOS features. Apple has released performance updates for UI components such as collection views, and it's not immediately apparent how quickly React Native libraries would take advantage of such changes. (I could look at React Native change history, but I haven't done so at this point.)
Conclusions
After diving into React Native, I've opted to climb back out of the pool for now. I see the benefits to writing an app once and having it available on iOS and Android. If I was looking at a certain type of app that only needed to use common UI interactions while consuming data from a back-end web service, I'd probably wade back into the React Native pool. However, I find myself wanting to stick with purely native development after hitting a few snags and seeing that I'm not going to necessarily get the same experience I will encounter with a purely native approach (unless I'm willing to do some work for it).
If I were a web developer jumping over to React Native because it allows me to leverage my JavaScript and React knowledge base, I'd want to spend some time becoming familiar with the possibilities when going completely native. There's a good chance the community components make certain choices that you may not see from the React Native side of things.
Technically, it's possible to expose all the native OS capabilities to JavaScript code, so it wouldn't be fair to say you're limited by React Native. However, accessing the full capabilities of the native platform requires an in-depth knowledge of the underlying platform. Without some work, a React Native app will likely be somewhat limited to the common features between iOS and Android without truly representing either one.
I'd still like to spend some more time with React Native to get a better grasp of how to bridge between JavaScript and native code. I'm also curious about debugging across native and JavaScript code. I want to understand more clearly how React Native apps work with native features, such as background fetch and push notifications. Finally, I'd like to see how React Native interacts with background threads in native code since JavaScript code all runs on a single thread. If you're debating use of React Native, it's certainly worth some investigation even if you decide not to dive in.