An iOS Developer’s Perspective on React Native – Part 2

Blog

April 7, 2016

TABLE OF CONTENTS

Introduction

In Part 1 of this series I compared native iOS development to React Native after briefly researching it. In Part 2 I will compare aspects of creating an app with basic UI and a native technology specific to iOS with both methods. For a real world comparison and context I will be recreating the Touch ID login experience from the Bank of America Mobile Banking application.

The Login Process

The overall navigation in the Bank of America iOS app is based on the hamburger or drawer navigation paradigm. In the case of the login process certain top level features are available at all times like ‘Help & Support’ and other privileged features like ‘Accounts’ are redirected to ‘Login’ because they are only available after a user is logged in. On the ‘Login’ screen the user can either enter their username and password or use Touch ID after enrolling to login. If the user tries to login with Touch ID and fails the user can then fallback to logging in with their username and password. After the user logs in they can view their accounts and other options on the ‘Accounts’ screen.

Native Development

To create the hamburger navigation I played with customizing a UISplitViewController but ultimately ended up using SWRevealViewController. SWRevealViewController was simpler to use than other hamburger navigation libraries I’ve used in the past. Just create a SWRevealViewController and give it an initial front view controller and either an under left or under right view controller. It even supports setting up your view stack in a storyboard which I used in this exercise. Our initial front view controller is the ‘Login’ screen. The way this is setup in the storyboard is by connecting the ‘Reveal View Controller’ storyboard scene (highlighted in blue in the image below) to the ‘Login’ scene with a relationship segue (also in blue). The initial under right view controller is the ‘Menu’ screen, also connected with a relationship segue. On the ‘Menu’ screen I’ve added the ‘Login’, ‘Accounts’, ‘Bill Pay’, and ‘Help & Support’ items in a static table view and their corresponding segues to their related screens. Note that ‘Bill Pay’, and ‘Help & Support’ scenes are not shown in the storyboard below.

image Screenshot

To enable the redirection of selected privileged features while the user is logged out I added a flag to the MenuViewController to keep track of the login state. In order to intercept the segue attempted to be performed when a user taps on a menu item I also overrode the shouldPerformSegueWithIdentifier method in MenuViewController. There I check if the user isn’t logged in and trying to access a privileged screen in which case I perform a different segue to the ‘Login’ screen and return false to cancel the current segue.

override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
    if isLoggedIn == false && privilegedSegueIdentifiers.contains(identifier) {
        let navigationController = self.storyboard?.instantiateViewControllerWithIdentifier("navSignIn")
        self.revealViewController().pushFrontViewController(navigationController, animated: true)
        return false
    } else {
        return true
    }
}

On the ‘Login’ screen I simply dragged out two text fields and a button and setup their constraints. I skipped the enrollment process for Touch ID so that the Touch ID prompt will be shown when the login button is pressed. Implementing Touch ID is as simple as importing LocalAuthentication, creating a context and evaluating the DeviceOwnerAuthenticationWithBiometrics policy in that content. On success the flag in MenuViewController is toggled to true and the segue to the Accounts screen is performed.

context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics,
                       localizedReason: "Logging in with Touch ID",
                       reply: { (success : Bool, error : NSError? ) -> Void in

                        dispatch_async(dispatch_get_main_queue(), {
                            if success {
                                let menuVC = self.revealViewController().rearViewController as? MenuViewController
                                menuVC?.isLoggedIn = true
                                let navigationController = self.storyboard?.instantiateViewControllerWithIdentifier("navAccounts")
                                self.revealViewController().pushFrontViewController(navigationController, animated: true)
                            }
                            // Handle errors here
                        })

})

image native1

React Native Development

To implement hamburger navigation on React Native I used the react-native-side-menu library. It abstracts the hamburger navigation to a Menu component and handy props for customizing its behavior through offset amounts and gesture settings. It works with React Native’s one-way data binding so that changing the side menu’s isOpen state through setState triggers a re-render. Similarly, adding a selectedItem state allows for communication between the menu and the front view when a user selects a menu item.

I filled out the ‘Login’ screen by creating some styles for the TextInput views and put them inside a Flexbox container that uses the default column layout and is aligned to center. The Menu is similar except the items are left aligned by default.

For adding Touch ID functionality I found react-native-touch-id library to work fairly simply. I just connect my login button to the _pressHandler function which calls TouchID.authenticate(‘String to show the user on Touch ID prompt’) which returns a promise. When the promise is fulfilled the selectedItem is changed to Accounts which triggers a re-render to show a ListView of accounts. One thing to note is that in _pressHandler to correctly bind this an arrow function expression is needed (my example is in ES6). This tripped me up for a moment as I wasn’t getting any errors when calling setState on the incorrectly bound this.

 _pressHandler = () => {
    TouchID.authenticate('String to show the user on Touch ID prompt')
    .then(success => {
      this.setState({
        isOpen: false,
        selectedItem: 'Accounts',
      })
    })
    .catch(error => {
    });
  };

image alt text

Comparison

Given my experience with building native iOS apps, the native app was easier for me to build. The hardest part was the hamburger navigation since it isn’t a standard container view controller and container view controllers can get quite complicated on iOS. Thankfully SWRevealViewController handles the easy cases of view controller containment.

On the react native side I learned a lot about laying out views with Flexbox. Flexbox may not be able to handle complicated circular layouts like UICollectionView can but when is the last time you saw a circular layout in an app? For most designs Flexbox is able to handle layout just fine. I would say that it is more complicated and more powerful than springs and struts but less so than Autolayout. Even though Autolayout is more complicated, I find it is easier to learn and use especially for simpler layouts. This partly stems from the ability to use Autolayout in at least 3 different ways and one of those ways is with a visual editor which generates XML layout code which looks like React Native Flexbox code if you squint whereas Flexbox on React Native can only be used one way I believe.

When it comes to updating the layout for a data change React Native’s one-way data binding is simpler to understand. Just update your state and wait for the re-render. A bigger change than layout is the declarative style of programming with React Native. This coupled with learning some Javascript was probably the biggest hurdle for me. I’m still wrapping my head around when to use props or context, converting older examples to ES6, and learning the JSX syntax.

Overall I’m very glad I did this experiment and I don’t think that if the entire Bank of America login experience was written using React Native that a user would notice any large differences between it and fully native app. With that said there are other places in the app where a user might notice a difference such as when searching transactions since React Native doesn’t natively have access to a UISearchController. Of course there are always compromises and if an app is already straying from platform conventions in major ways such as using hamburger navigation and non-standard back buttons and gestures then building it with React Native shouldn’t be a problem if the trade offs are understood by everyone involved.

Authored By

Steve Moser, Engineering Manager

Steve Moser

Engineering Manager

Meet our Experts

Steve Moser, Engineering Manager

Steve Moser

Engineering Manager

Steve Moser is an Engineering Manager at Levvel focusing mainly on iOS. He is passionate about leveraging software to deliver great experiences.

Let's chat.

You're doing big things, and big things come with big challenges. We're here to help.

Read the Blog

By clicking the button below you agree to our Terms of Service and Privacy Policy.

levvel mark white

Let's improve the world together.

© Levvel & Endava 2023