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.

UIImagePickerController UI
react-native-camera UI

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.

UIImagePickerController UI
React Native Custom UI

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.