An iOS Developer’s Perspective on React Native

With React becoming more and more popular I decided to see what all the fuss was about, and thought I might share a native iOS developer’s perspective.

What is React Native?

React Native is a library by Facebook built on top of React that allows developers to write native apps (currently iOS and Android) using JavaScript or rather JSX. It is not a ‘write once, run anywhere’ solution, even though it is possible to share code between platforms. React Native’s mantra is ‘learn once, write anywhere’. In practice, what this looks like (at least for Facebook) is a single team that works on both the iOS and Android versions of the app that are tailored somewhat for each platform. In order to do that, every team member must be familiar enough with the conventions of each platform to do a smoke test after changes are made to code that is shared between platforms. So far, Facebook has used React Native for their Facebook Groups app and their Ads Manager app. To see what a React Native app feels like check out the featured React Native apps.

Why use React Native?

To understand why one might want to use React Native, you have to understand why you might not want the default option: a 100% native Objective-C/Swift app. Facebook cites difficult layout, slow recompilation times, and comparatively slower releases (than web) as their mains issues with native app development. Making changes and recompiling to see the effect of those changes can take sometime, especially if your application has 18,000 classes. Hybrid app solutions like Ionic fix slow recompilation times by making changes available after a simple refresh since the modified JavaScript doesn’t need to be recompiled. One problem with traditional hybrid mobile app frameworks is their performance because everything lives inside of a web view. Web views don’t provide buttery-smooth 60fps scrolling and elegant touch handling. Another potential issue is that web views can only mimic native UI components. Achieving a pixel perfect reproduction of native UI is a fleeting dream with all the minor UI changes Apple makes between iOS system updates.

Facebook built React Native to solve most of the native and hybrid app development issues while bringing along the benefits of React’s declarative, predictable UIs. React Native keeps the Ionic’s fix for slow recompilation times but solves some of the performance issues of coding a native app in JavaScript by communicating to JavaScriptCore via a batched, asynchronous messaging protocol to prevent blocking of the main thread. Since React Native uses native views instead of web views, touch handling is excellent and there is no need for imperfect and ever-changing UI mimicry. Also, layout is made easier for developers who are familiar with Flexbox by reimplementing it for React Native.

How does React work in practice?

To try React out and understand what it means for designers, developers, and users I decided to try to tackle the same challenge I recently had with a design request for a modal search box within an app I was working on. Usually in a native iOS app searching is made available through an inline search bar at the top of a scroll view. However, the screen that I needed to add the search capability to wasn’t a scroll view, so I needed to modally present a search bar after the user tapped on a magnifying glass icon in the navigation bar. It was suggested that I make it work like the native Calendar app for iOS where the search bar animates down from above when it is presented. The final requirement was that a detail view controller should be pushed onto the modal navigation controller stack after a result was tapped, again, just like the native Calendar app.

The Native Approach

The first part was fairly straightforward when developing the app using Objective-C. The starting view with the magnifying glass search icon was contained in a UINavigationController so in one line I could call:

self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSearch 
target:self 
action:@selector(search)];

or in swift:

navigationItem.rightBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .Search,
            target: self,
            action: "search:"
        )

Alternatively, this could be setup via a storyboard.

Next, I had to nicely animate the search bar from the top just like the Calendar app so I simply modally presented a configured UISearchController with presentViewController: on my base view controller. The non-linear animation to drop in the UISearchBar is contained within UISearchController so there is no need to guess at the animation settings or update the app every time Apple tweaks the animation with an update.

The final part was the trickiest. We wanted to push a detail view controller into view when the user selects a result, but we didn’t have a UINavigationController to push onto in our modal view. This UI flow is present in Apple’s Calendar and Music so I thought that there must be an easy way to accomplish this task. I reached out to Apple’s Developer Technical Support (DTS) to make sure I wasn’t missing something simple but they replied that the push functionality in Calendar and Music used private methods. DTS suggested I instead dismiss the UISearchController after a selection is made and then push a view controller onto the original UINavigationController stack while keeping track of the search-in-progress with a flag. This way, the UISearchController is presented again after the user pops the detail view controller. I came up with a different solution which keeps the user in the flow by setting the UISearchController‘s search results controller property to a UINavigationController so that the detail view controller can be pushed onto that modal UINavigationController. I still get the nice drop in animation for the search bar, but I have to hide the search bar on selection of a result and unhide it when the detail view controller is popped. This is less than ideal since the showing of the search bar doesn’t participate in the interactive swipe-to-go-back gesture. This is still better for my use case than modally presenting the search bar after popping the detail view controller because that modal presentation doesn’t participate in the swipe-to-go-back gesture. You can see the result of the approach I took above.

The React Native approach

I looked at some example React Native apps since I don’t have much React or React Native experience and I found UIExplore in the React Native Github repo. The starting view has a box to type a search query into, which then filters the original list. This seems like a good starting point. First I needed to clean up the search box. The example uses a UITextField instead of a UISearchBar. I pulled up the React Native documentation for UISearchBar and received zero results. No official React Native support is what you’ll find for a large number of native UI components. The example uses UINavigationController, but the docs for the React Native equivalent — NavigatorIOS — lists several issues when using UINavigationController, such as a backlog of issues that the React Native team isn’t looking into:

For most non-trivial apps, you will want to use Navigator – it won’t be long before you run into issues when trying to do anything complex with NavigatorIOS. – React Native Docs

From this and other things I’ve read about React Native, I gather that a lot of the native iOS views, especially ones with controller in their name, don’t fit well into the declarative UI’s that React Native constructs.

From here, I could write the adapter code needed to make UISearchBar available to React Native, but someone in the community has beaten me to it. It’s great that this is available, but relying on over 300 lines of community maintained code that lives on top of a completely different paradigm for writing natives for a basic UI component doesn’t instill confidence in me. Tasks that are simple in Obj-C/Swift, such as creating the magnifying glass icon, routinely turned into roadblocks. The NavigatorIOS class doesn’t have a hook for the initWithBarButtonSystemItem: method. Also, there isn’t a community library for UISearchController because once again most native view controllers don’t fit well into the declarative UI model.

A few other areas where you might find it challenging to develop with React Native is with Dynamic Type and custom view controller transitions. React Native currently doesn’t support Dynamic Type because React Native styles text in a web orientated way with font families and sizes where Dynamic Type uses text styles. Custom View Controller transitions can be tricky for React Native when processing incoming data at the same time. Facebook’s post covers the issue the best:

There’s only one way that navigation animations could stutter, and that’s when the JavaScript thread was blocked during a big operation. When we encountered this scenario, it was almost exclusively due to processing large amounts of newly fetched data. Of course, it makes sense that when you navigate to a new view, more data has to be loaded and processed. On a sufficiently fast network, that process could easily interfere with a navigation animation still in progress. Our solution here was to explicitly delay the data processing until animations were complete, using the InteractionManager component, which also ships as part of React Native. We would first animate to a view that contained placeholders and then let Relay do the data processing, which automatically caused the necessary React components to re-render. – Daniel Witte & Philipp von Weitershausen

TL;DR

Let me first state that I still have much to learn about React Native. From my limited understanding, React Native works great for a development team that already knows some React and wants to make a performant mobile app. It also works well for an organization that doesn’t want to bifurcate their development teams (iOS and Android) and is already proficient in designing, creating, and maintaining websites.

On the other hand, there is a substantial learning curve to adopting React Native if your team is not familiar with React or web development, as they will have to learn React, JSX, JavaScript, and Flexbox in addition to the applicable Android/iOS conventions and frameworks. Thankfully, React Native allows for incremental adoption. However, if I were to introduce React Native to an organization I would try to set it up for as much success as possible by incorporating as many points as possible listed below:

Go all-in on React Native by starting a greenfield app for both iOS and Android that doesn’t require many platform exclusive mobile features such as NFC for Android or iBeacon for iOS. Optimize the design and development of the app for maximum reuse which means limiting the use of iOS or Android only React Native components. Have those familiar with React lead the development effort and set aside time for knowledge transfer both ways between web and native developers. Make sure everyone is familiar with the platforms by setting up every developer with devices from each platform so that they not only learn that platform but also are able to test their changes on both platforms. Set up a continuous integration server and have developers implement a cross-platform feature and write tests for the platform they are not as familiar with.

I admit that when first looking into React Native I thought it was just a fancier way to write PhoneGap/Cordova apps. Let’s just say I was quite pleasantly surprised. However, if you want the most native experience today and native development works well for your organization then stick to native development while always investigating ways to improve your process and tooling.

Steve Moser

Senior Consultant

Steve Moser is a Mobile Application Developer at Levvel focusing mainly on iOS. He is passionate about leveraging software to deliver great experiences.

Related Posts