Creating A React Native App for iOS

Spread the news

React Native is a great JavaScript framework for creating cross-platform, native apps. With one single codebase you can support both Android and iOS. A fellow PHP developer, Jordan Walke, created React to help manage the front end of Facebook back in 2011. React has since been expanded and also spawned its sidekick React Native.  There are multiple reasons to use React Native as opposed to some other cross-platform tool:

  • Components in React get translated to real, native components in the final application.  This creates a truly “native” feel since those components are not re-created.
  • React Native allows for a single codebase for all platforms, but also allows the developer to easily separate out any component to make it work slightly different on different platforms.
  • React Native is written in JavaScript, which makes it pretty easy to learn.  According to Stack Overflow, JavaScript is the most popular programming language of 2017, though not everyone agrees completely with their study.  However, there is no doubting that JavaScript is known by most developers, making the world of app development open to many more people by allowing it to be written it in JavaScript.
  • JavaScript has many libraries that can be utilized to extend your app easily – and React Native is not excluded.  Certainly, since these Apps are not running in a browser, things like jQuery would be useless, but many other libraries can easily be ported to or included in React Native, such as lodash, for example.

Let’s look at how to create a React Native application, focusing on iOS (but that is completely possible to run on Android as well)!

Getting Started – Setup

In order to create a react native application, we will have to have some basic things installed on the computer.  You can choose to develop on any platform as JavaScript is completely system agnostic! I use Mac for everything, though, so you might notice a lot of Mac terminology here.  If something is confusing you, please leave a comment below and I’ll be sure to respond and try to point you in the right direction.

Step 1: Install Node. This will give you access to npm, which we will use to do everything else. There are not any major tricks to this – it’s pretty simple.

Step 2: Install the React Native simple creation tool.  From a terminal install ‘create-react-native-app’ as a global package so you can use it to initialize a new project: npm install -g create-react-native-app.

Step 3: Install yarn.  This can be done via npm but is not recommended by for security reasons.  Head over to their install page for ways to install on your system. If you really don’t care about security and want to get to playing fast, npm install -g yarn will get it on your system.

That is really all there is to it – you are done setting up.  Now you can create your first react native app!

Creating a New App

For the purposes of this tutorial I am going to be working through how to create a “spin the bottle” type app.  Originally I was thinking of making something like Wheel of Fortune, but later decided that was too big of a project for this tutorial.  I stuck with the concept of spinning a wheel and came up with this very simple concept of the old, classic, bad-teenager game of spin the bottle.  The concept is simple and the requirements are simple. It will demonstrate several principles of React Native while creating a not-so-useful, but completely-functional app!

The first step is to make a new application, which is very simple.  Navigate to the directory you want to put your new app in with the terminal, and run the React Native helper we just installed using npm.  Then drop into the directory this app created and run the command yarn start:

cd ~/dev
create-react-native-app SpinTheBottle
cd SpinTheBottle
yarn start

This last command, yarn start will start a small development server which uses a service called “watchman” to compile any changes to the application code and send it to a special development app called Expo.

You can download Expo from the app store and then use the “s” function to send yourself a link via text message or email that can be opened to run the app you are creating natively on your phone.  Expo used to have a handy scanner for the QR code that appears on your screen, but recently had to take it out of the iOS version.  It should still exist on the Android version.  The only catch is that the phone must be on the same exact network as, and have access to, the development machine. There shouldn’t be any problem if both are connected to the same wifi hotspot, but that all depends on the hotspot’s setup.

Alternatively, you can press “i” to open the iOS emulator.  This function only works if you have xcode set up and have opened it and accepted the license agreement.

Congratulations! You now have an app running on your phone using JavaScript!

Importing Components and Showing Images

The first thing we need for a spin the bottle app is a picture of a bottle.  To make this simple I just used Google Image and found a simple bottle on a white background. Let’s create an img directory in the SpinTheBottle directory and save the image there as bottle.jpg.  Now we can make the image show up in our app.

Since I’m a PHP developer I tend to use PhpStorm from JetBrains for anything and everything.  It handles JavaScript nicely and does the trick perfectly.  All we have to do is click “Create New Project from Existing Files” and point it at the SpinTheBottle directory. It automatically detects the node packages, and also facilitates using yarn or npm right from within the IDE.  Expo has also created an IDE that gets good reviews – I would use that if I didn’t have access to PhpStorm!

Inside the App.js file there is a method called render which is what gets called whenever the screen needs updated (because some information somewhere changed). This render method returns some special HTML-like components which will get placed on the screen following a FlexBox style. FlexBox has a lot in common with the web technology, but is not exactly the same. You can find a lot more information about components and styles in the React Native documentation.

We can completely remove the <Text> components and replace them with an image: <Image source={require('./img/bottle.jpg')} />.  Up at the top we will also have to add Image to the list of React Native components to import – just add it to the list with a comma, keeping it inside the curly braces. If you save the file and go look at your app you should now see your image.  It might not be sized correctly yet, but it should be there.  There are a few additional props (short for properties) available on the Image component that can help us position and size it.  The first thing we will want to use is resizeMode.  This prop allows us to tell the Image component how to resize the image that it contains.  We want this image to always fill the screen regardless of what size the screen is – whether it’s an iPhone 5 or an iPhone X! If we use resizeMode={'contain'} we will get this functionality for free:<Image resizeMode={'contain'} source={require('./img/bottle.jpg')} />. Again, if you save the file your Expo app should automatically detect the update and should now display the image resized to fill the screen.

Now we have a problem.  If we are going to be rotating (spinning) this bottle, we won’t want it to go off the screen, so what we really need is to resize it down to a square…  but the square needs to fit on any and every screen as well.  Fortunately there is another API available to import called Dimensions.  If we add it to the import statement at the top, then we can get the height and width of the screen with a call var {height, width} = Dimensions.get('window');. This will put the height and width of the available screen window in their corresponding variables. We can make the size a little smaller and then use that to set the size of our image component.  Fortunately the resize mode we have used will cause the image to shrink to fit this new size as well.  We can add styles through the StyleSheet seen at the bottom of the file, but we can also pass an object directly into the style prop on the Image component for the same effect.  Since we are computing the size, we will need to put this style directly in the code using a generic object.

At this point we should have an import at the top bringing in Image and Dimensions: import { StyleSheet, Text, View, Image, Dimensions } from 'react-native';, and a render function that looks like this:

    render() {
        var {height, width} = Dimensions.get('window');
        height = height * .9;
        width = width * .9;
        return (
            <View style={styles.container}>
                <Image style={{width, height: width}} resizeMode={'contain'} source={require('./img/bottle.jpg')}/>
            </View>
        );
    }

At this point the image is on the screen and should be the correct size for spinning.

Responding to Touch

We will need some way for the user to interact with the bottle and spin it.  React Native has multiple components that respond to touches.  The simplest is to surround something with the <TouchableOpacity> component. Opacity is just describing that the touch will give you some opacity response to the touch for free, and makes the app feel more responsive than just using a <Touchable> component. Touchable requires a prop that defines a function to run when pressed.  This is defined in the onPress prop and is usually just an anonymous function.  For now we can just have it alert us to the press…

    render() {
        var {width} = Dimensions.get('window');
        width = width * .9;
        return (
            <View style={styles.container}>
                <TouchableOpacity onPress={()=>{alert('pressed')}}>
                    <Image style={{width, height: width}} resizeMode={'contain'} source={require('./img/bottle.jpg')}/>
                </TouchableOpacity>
            </View>
        );
    }

At this point, the phone should be responding to touches! It should change the opacity of the image and also pop up a simple alert that says “pressed.” Simple enough!

Using State and Rotating the Image

State is an important concept in React Native.  If a component has state, then changing the state will cause the render method to get called again.  As you can guess, this is how we can cause stuff to update.  The concept of the entire structure behind React Native is that the render method is a fancy way to show something on the screen based on whatever is in the state.  So far we have never put anything in the state, so technically our render method is only getting called once.  Let’s add state to our app so we can use it to determine what direction the bottle is pointing.

To get access to state we need to create a new method called constructor that gets called whenever this component is instantiated.  Inside this constructor we will want to first call the parent constructor by calling super(); and then define state very simply as an object.  This is the only place in an app that the state should be changed directly.  Any other change to state MUST be done with another method, but in the constructor we can set it very simply as an object:

    constructor(){
        super();
        this.state = {
            bottleRotation: 0
        }
    }

Now that we have this, we can read from the state object anywhere. So, down in our render method we can get the bottleRotation and use it to rotate the image via the style prop on the Image:

render() {
    let {width} = Dimensions.get('window');
    width = width * .9;
    let bottleRotation = this.state.bottleRotation.toString() + 'deg';
    return (
        <View style={styles.container}>
            <TouchableOpacity onPress={()=>{alert('pressed')}}>
                <Image style={{width, height: width, transform:[{ rotate: bottleRotation }] }} resizeMode={'contain'} source={require('./img/bottle.jpg')}/>
            </TouchableOpacity>
        </View>
    );
}

At this point you should be able to play around with rotation by changing the 0 in the constructor to any other number to rotate it that many degrees.

Now we can utilize our state to redraw the screen with a newly rotated image any time the user presses the image! We will need another method which we can call rotateBottle. All this method needs to do is get a random number from 0 to 360 (degrees in a circle) and update the state. We can then call the method from our TouchableOpacity component:

rotateBottle(){
    let newDeg = Math.random() * 360;
    this.setState({bottleRotation: newDeg})
}

render() {
    let {width} = Dimensions.get('window');
    width = width * .9;
    let bottleRotation = this.state.bottleRotation.toString() + 'deg';
    return (
        <View style={styles.container}>
            <TouchableOpacity onPress={()=>{this.rotateBottle()}}>
                <Image style={{width, height: width, transform:[{ rotate: bottleRotation }] }} resizeMode={'contain'} source={require('./img/bottle.jpg')}/>
            </TouchableOpacity>
        </View>
    );
}

Animation!

At this point we have a functioning spin the bottle app… well, at least the end result is the same. However, it looks pretty dumb to just redraw the image! We need to make it rotate around to the new position. The easiest way to accomplish this is with React Native’s Animated API. It includes everything we need to gradually change the state over time, causing things to move fluidly.  We will need to substitute several things we have currently for “Animated” functionality.

First, we’ll need to add Animated to the list of imported components.  Then we can change the Image component to an Animated.Image component (just type “Animated.” in front of Image!!) and change our bottleRotation property in the state to be a new type of object called an Animated.Value.  We can set the initial value to 0 with the following constructor code:

    constructor(){
        super();
        this.state = {
            bottleRotation: new Animated.Value(0)
        }
    }

We also need to change how we use the variable in the render function using the interpolate method that is now available from this.state.bottleRotation which gives us a new render method that looks like this:

render() {
    let {width} = Dimensions.get('window');
    width = width * .9;
    const bottleRotation = this.state.bottleRotation.interpolate({
        inputRange: [0, 360],
        outputRange: ['0deg', '360deg']
    });
    return (
        <View style={styles.container}>
            <TouchableOpacity onPress={()=>{this.rotateBottle()}}>
                <Animated.Image style={{width, height: width, transform:[{ rotate: bottleRotation }] }} resizeMode={'contain'} source={require('./img/bottle.jpg')}/>
            </TouchableOpacity>
        </View>
    );
}

Lastly, we need to change how we set the rotation:

    rotateBottle(){
        let newDeg = Math.random() * 360;
        Animated.timing(this.state.bottleRotation, {
            toValue: newDeg,
            duration: 1000
        }).start();
    }

At this point it will rotate from one degree to another over a one second time. This is nice and all, but that’s not exactly how a bottle spins in spin the bottle! If we want it to spin around a couple times then we will have to offset the degrees with additional degrees! If 360 degrees is one revolution, then 720 degrees would be two full spins. For example, instead of going from 0 to 30, we can go from 0 to 750 and have the bottle pointing the same direction. I believe we are asking for trouble with overflows doing that, so let’s make it add and then subtract, back and forth – a simple if statement should do the trick:

    rotateBottle(){
        let newDeg = Math.random() * 360;
        const curValue = this.state.bottleRotation.__getValue();
        if ( curValue > 720 ){
            newDeg = curValue - newDeg - 720;
        } else {
            newDeg = curValue + newDeg + 720;
        }
        Animated.timing(this.state.bottleRotation, {
            toValue: newDeg,
            duration: 1500
        }).start();
    }

We now have a simple app that displays a bottle and responds to touch by spinning the bottle back and forth to a random direction.

This app can now be compiled and sent to TestFlight for iOS and then submitted to the app store. This can be done in a couple of ways. If you want to stick with Expo, then you would use the exp command to submit it to expo for compiling. That will generate a file you can download and submit. Otherwise you would follow the instructions for “ejecting” the app from expo which will create an xcode project that can be opened directly and used from within XCode. If you plan on doing any deeper stuff that needs to have some deep level coding in Objective C/Swift, then you will be forced to use the second method, but most people in the react native community never eject their code, and use Expo from start to finish!

Have anything to add or a question to ask? I would love to hear it in the comments section below!

This tutorial was created as an overview of the talk I gave at Las Vegas iOS Developers! You can find this code on my github (each section of code is a tag).


Spread the news

Leave a Reply

Your email address will not be published. Required fields are marked *