How to build, bundle and test React Native projects on Bitrise

Build on iOS and Android platforms

Short Summary

Building React Native project is basically the same as building for the target platform with a little twist. When you add a new app on Bitrise.io you will see the project scanner will detect iOS and Android projects. This is because the structure React Native uses is the same as in a simple iOS or Android project. And now:

The twist

React Native’s CLI tool has a bundle command, which will create bundle from your JS files. (index.android.js & index.ios.js). When you init a new project with react-native init you will get an iOS and an Android project base as well. These project bases are a fully build-able “frameworks” for both platform, and will display and run your project bundled in and if it is injected to your Xcode or gradle project. React Native has splitted bundle handling into two ways:

  • Debug build (online app): a framework app will be built with an url pointing to your bundle. This way you need to build Xcode or gradle projects only once then install it on a device, and the rest of the testing is to bundle your project to where your app’s url points. (You can easily save a lot of time with your tests this way actually)
  • Release build (offline app): In the projects it is automated when you build with release configuration, the bundle will be automatically included in the built app. So you won’t need the bundle over an url, it comes with the app.

So when you need a debug build, and still an offline app you will need to bundle your React Native project, and copy the bundle into the Xcode or gradle project.

Thanks to this little twist, we’ll need to add steps to the generated Xcode, or Android workflow. On both platform you will need to add:

  • Install React Native step (can be right after git clone step)
    This step will install react-native (required to run bundle command in build processes)
  • Run npm command step (before xcode or gradle build steps)
    Type install in command input. Before running npm install make sure it will run in your React Native project’s root directory. (You can use Change working directory step in case your project is not in the root of your git repo, or you can set Workdir input to your project)

An example bitrise.yml of building a release iOS app:

---
format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
workflows:
  ios_release:
    steps:
    - activate-ssh-key@3.1.1:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@3.4.2: {}
    - install-react-native@0.9.1: {}
    - npm@0.9.0:
        inputs:
        - workdir: "$PROJECT_DIR"
        - command: install
    - certificate-and-profile-installer@1.8.4: {}
    - xcode-archive@2.0.5:
        inputs:
        - export_method: development
        - workdir: "$PROJECT_DIR/ios"
        - project_path: "./SampleAppsReactNativeAndroid.xcodeproj"
        - scheme: SampleAppsReactNativeAndroid
        - configuration: Release
        - is_export_xcarchive_zip: 'yes'
    - deploy-to-bitrise-io@1.2.9: {}
    before_run: 
    after_run: 
app:
  envs:
  - opts:
      is_expand: true
    PROJECT_DIR: "$BITRISE_SOURCE_DIR"

Bundle

This won’t be a headache. Bundle with React Native can be set up using only couple of steps after git clone:

  • Install React Native step
  • Run npm command step - command: install - workdir: your project’s root directory
  • React Native Bundle step

An example bitrise.yml to get an android bundle:

---
format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
workflows:
  primary:
    steps:
    - activate-ssh-key@3.1.1:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@3.4.2: {}
    - install-react-native@0.9.1: {}
    - npm@0.1.1:
        inputs:
        - command: install
        - workdir: '$BITRISE_SOURCE_DIR'
    - react-native-bundle@1.0.3:
        inputs:
        - platform: android
        - out: "$BITRISE_DEPLOY_DIR/index.android.bundle"
        - dev: 'false'
        - assetRoots: "$BITRISE_SOURCE_DIR/android/app/src/main/res/"

This would be a great feature, if you can upload the bundle with ftp or any 3rd party sharing service, and point your iOS and Android app’s url to the uploaded bundle. This way you won’t need to build the apps every time still you can see your React project in the framework apps. Great! :tada:

Test

There are multiple ways you can test your project, the following lines are explaining React Native’s most common, built-in method. This is jest. When you react-native init a new project, you will get a project including packages.json, including npm task test.

So basically you will need to run an npm command test in your project’s root directory to start the built-in test method.

To do this, you will need some preparation steps after your git clone step:

  • Install React Native step
  • Run npm command step - command: install - workdir: your project’s root directory
  • Run npm command step - command: test - workdir: your project’s root directory

Here is an example bitrise.yml of running built-in jest test:

---
format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
workflows:
  test:
    steps:
    - activate-ssh-key@3.1.1:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@3.4.2: {}
    - install-react-native@0.9.1: {}
    - npm@0.9.0:
        inputs:
        - workdir: "$PROJECT_DIR"
        - command: install
    - npm@0.9.0:
        inputs:
        - workdir: "$PROJECT_DIR"
        - command: test
    before_run: 
    after_run: 
app:
  envs:
  - opts:
      is_expand: true
    PROJECT_DIR: "$BITRISE_SOURCE_DIR"

Feel free to ask any questions :wink: Yep, and if you have an experience with React Native projects already, please share it with other users! :rocket:

2 Likes

This could be just

- project_path: "./SampleAppsReactNativeAndroid.xcodeproj"

As the path is relative to the workdir :wink:

I’d also remove the empty inputs like

that would make the config shorter, and easier to understand :wink:

Just a question @tamaspapik - should I commit my Xcode project into the repository, or should I gitignore it?

1 Like

Good question!

Short answer:
You should commit xcode and gradle project into your repository, and shouldn’t be gitignored.

Long answer:
To be able to bundle your project, in react-native bundle you will need to set a flag --assets-dest, and also React Native Bundle step has this input you have to set.

  • for index.ios.js: ./ios
  • for index.android.js: ./android/app/src/main/res

There are files needed to be included in the bundle from these folders!
So it should be committed to the repository.

1 Like

Absolutely! Post updated :rocket:

1 Like

First of all, thank you for this guide :+1:

I’m stuck trying to build a release version (with bitrise) and I think I need help.
I can’t seem to deploy a Release version (built with bitrise) of my app on my phone without it crashing instantly on launch :frowning2:

I can deploy a Release version locally from xCode without any issues.

I have BugSnag installed and it is showing this:

RCTFatalException: Unhandled JS Exception: invalid host·Unhandled JS Exception: invalid host

Looking at the device logs is not helping much but hinting that React is throwing an exception which is what BugSnag is saying too:

Thread 5 name:  Dispatch queue: com.facebook.react.ExceptionsManagerQueue
Thread 5 Crashed:
0   libsystem_kernel.dylib        	0x000000018ddab014 __pthread_kill + 8
1   libsystem_pthread.dylib       	0x000000018de73450 pthread_kill + 112
2   libsystem_c.dylib             	0x000000018dd1f400 abort + 140
3   libc++abi.dylib               	0x000000018d7e92d4 __cxa_bad_cast + 0
4   libc++abi.dylib               	0x000000018d806cc0 default_unexpected_handler() + 0
5   libobjc.A.dylib               	0x000000018d814844 _objc_terminate() + 124
6   Foodzilla                     	0x0000000100175b64 0x100004000 + 1514340
7   libc++abi.dylib               	0x000000018d80366c std::__terminate(void (*)()) + 16
8   libc++abi.dylib               	0x000000018d802f84 __cxxabiv1::exception_cleanup_func(_Unwind_Reason_Code, _Unwind_Exception*) + 0
9   libobjc.A.dylib               	0x000000018d814690 _objc_exception_destructor(void*) + 0
10  Foodzilla                     	0x000000010004f43c 0x100004000 + 308284
11  Foodzilla                     	0x000000010004ec6c 0x100004000 + 306284
12  libdispatch.dylib             	0x000000018dc661fc _dispatch_call_block_and_release + 24
13  libdispatch.dylib             	0x000000018dc661bc _dispatch_client_callout + 16
14  libdispatch.dylib             	0x000000018dc743dc _dispatch_queue_serial_drain + 928
15  libdispatch.dylib             	0x000000018dc699a4 _dispatch_queue_invoke + 652
16  libdispatch.dylib             	0x000000018dc748d8 _dispatch_queue_override_invoke + 360
17  libdispatch.dylib             	0x000000018dc7634c _dispatch_root_queue_drain + 572
18  libdispatch.dylib             	0x000000018dc760ac _dispatch_worker_thread3 + 124
19  libsystem_pthread.dylib       	0x000000018de6f2a0 _pthread_wqthread + 1288
20  libsystem_pthread.dylib       	0x000000018de6ed8c start_wqthread + 4

Here is my bitrise.yml

format_version: 1.3.1
    default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
    trigger_map:
    - push_branch: "*"
      workflow: primary
    - pull_request_source_branch: "*"
      workflow: primary
    workflows:
      primary:
        steps:
        - activate-ssh-key@3.1.1:
            run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
        - git-clone@3.4.2: {}
        - install-react-native@0.9.1:
            inputs:
            - version: ''
            - npm_options: ''
        - script@1.1.3:
            title: NPM Install
            inputs:
            - content: |-
                #!/bin/bash
                # npm install
                npm install
            - working_dir: "$BITRISE_SOURCE_DIR/Foodzilla"
        - script@1.1.3:
            title: RN Link
            inputs:
            - content: |-
                #!/bin/bash
                # Link projects
                cd /Users/vagrant/git/Foodzilla && react-native link
        - certificate-and-profile-installer@1.8.4: {}
        - script@1.1.3:
            title: Bundle
            inputs:
            - content: |-
                #!/bin/bash
                # RN bundle
                cd /Users/vagrant/git/Foodzilla && react-native bundle --entry-file index.ios.js --platform ios --bundle-output ios/main.jsbundle --dev false --assets-dest ./ios
        - xcode-archive@2.0.5:
            inputs:
            - team_id: ''
            - workdir: "$BITRISE_SOURCE_DIR/Foodzilla"
            - configuration: Release
            - force_team_id: ''
            - force_code_sign_identity: ''
            - force_provisioning_profile_specifier: ''
            - force_provisioning_profile: ''
            - custom_export_options_plist_content: ''
        - appetize-deploy@0.1.3:
            inputs:
            - app_path: "$BITRISE_APP_DIR_PATH"
            - appetize_token: tok_XXXXXXXX
            - public_key: ''
        - deploy-to-bitrise-io@1.2.9: {}
        before_run: 
        after_run: 
    app:
      envs:
      - opts:
          is_expand: false
        BITRISE_PROJECT_PATH: Foodzilla/ios/Foodzilla.xcodeproj
      - opts:
          is_expand: false
        BITRISE_SCHEME: Foodzilla

I suspect the archive generated by bitrise is not correct because local xCode builds work fine. I have bundled my app after the npm install and been trying all day to get this to work :disappointed:

Can anyone help me please?

Cheers :slight_smile:

For release versions, you won’t need anything to bundle, react-native has a build script built in your Xcode project, which will do everything for you what is required to build a release version.

Before your xcode-archive you can remove the bundle script which contains:

The other thing to do is to set your scheme in your xcode-archive step.

If it still doesn’t help, please contact me on the on-site chat. We will figure it out. :slight_smile:

Here’s some React Native & Bitrise tutorials.

1 Like

Thanks for sharing @John21!

Indeed this discussion was updated quite a long time ago, you can also find additional tutorials at: http://devcenter.bitrise.io/tutorials/community-created/#react-native

And a blog post was also just shared: https://blog.bitrise.io/bitrise-codepush-react-native-ideal-mobile-ci-setup

1 Like