Implementing Code Push in React Native applications
Tags: reactnative, react, codepush, devops
Published: 2020-06-23 06:23:54

Let me start by saying that this is an article for those who've never tried to implement Code Push or just want to find out more about it. I've heard about Code Push services many times before and it is one of the biggest features of Expo, but I've never tried to get one on `react-native init app`. I will talk about my motivation for implementing Code Push and the things that I found interesting and useful along the way.

## Why is Code Push good?

I'm not a fan of production bugs. If a bug somehow manages to get into production (and it does), you are under pressure to fix it and to deploy a bugfix update across devices as soon as possible. The deployment part is a hassle, because you need to build iOS version, Android version, find and access developer accounts, make sure that version numbers are correct, wait for the app review from the stores, etc. and it is time-consuming.
To make deploying part easier, a service called Code Push comes to help. It is a cloud service from [Microsoft Visual Studio App Center](https://appcenter.ms/), that acts as a central repository where developers can publish certain updates. These updates are then queried by apps using the client Code Push SDK. In other words, you create a JavaScript bundle, which is published to the Code Push service, and you configure your client app to load JavaScript builds from Code Push repository of builds.

## How to setup Code Push

#### Create an account
To start with Code Push you need to create an account on [Visual Studio App Center](https://appcenter.ms).
Along with Code Push, Visual Studio App Center offers creation of app binary builds (apk, aab, ipa), crash and usage analytics, testing of app on real devices, and distribution management. You will see all these features on the App Center dashboard when you create an app in the App Center. Code Push is free for an unlimited number of builds and devices.

#### Create an app

When you add a new app in App Center, it is recommended to create a new app for each platform, one app for Android, one app for iOS. While creating a new app you will be asked for the name of your new app, release type, OS, and Platform. OS is Android or iOS, and Platform is React Native, and release type is for the label only, I have named mine Production. After you have created an app, in a sidebar, go to Distribute > Code Push, there you will have a list of all Code Push releases you have created.

#### Setup Client SDK

Before we create a release for Code Push, let's implement Code Push SDK to our React Native app. The package needed for Code Push in React Native apps is [react-native-code-push](https://github.com/Microsoft/react-native-code-push). Before you start implementing Code Push SDK, [check the corresponding Code Push version](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-overview#supported-react-native-platforms), depending on your React Native version. My version of react native is 0.62.2 so I will use 
`react-native-code-push@v6.2` .
After installing the NPM package, you need to install the native module, so follow the [iOS setup and Android setup](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-get-started). For Android the process is similar to others, adding some lines to `android/settings.gradle` , `android/app/build.gradle`, `MainApplication.java` and `strings.xml`. In the `MainApplication.java` the most important change is

```java
/// MainApplication.java
...
@Override
    protected String getJSBundleFile() {
        return CodePush.getJSBundleFile();
    }
...
```
These line tells that the JS bundle is not provided by a local JS bundle that is in the app binary, but from Code Push.
In `strings.xml` the most important thing is *CodePushDeploymentKey*. You can get it with Code Push CLI, but also you can find it in the App Center app dashboard, under Code Push section, click on the options icon which is next to deployment selection. This is the key that identifies the deployment, and with that key app knows from which deployment to look for updates. By default, there are two deployments, Staging and Production. This enables you to have testing, staging, production, and other versions of the app, so that you can send a Code Push update only to specific deployment of the app. The guide for Code Push has an entire section for managing [multiple deployments](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-deployment#multi-deployment-testing). For trying-out purposes, my advice is that you don't define *CodePushDeploymentKey* inside `strings.xml`, but in `android/app/build.gradle`, under *buildTypes* section. By default there are two build types: *debug {}* , and *release {}*, and you can define the *CodePushDeploymentKey* as 
`resValue "string", "CodePushDeploymentKey", '"<< your CodePushDeploymentKey >>"'` , for each build type. My *buildTypes* looks like this.

```json

// android/app/build.gradle
    
buildTypes {
    debug {
        signingConfig signingConfigs.debug
        resValue "string", "CodePushDeploymentKey", '""'
    }
    release {
        signingConfig signingConfigs.debug
        minifyEnabled enableProguardInReleaseBuilds
        proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        resValue "string", "CodePushDeploymentKey", '"<< CodePushDeploymentKey >>"'
    }
}

```

You can see that for debug I left *CodePushDeploymentKey* empty so that I don't get any interaction with Code Push in the debug/development mode. Also, if you have another build type, here you can add the deployment key for that build type. The good thing to know is that the deploymentKey can be defined by script, with [codePush options](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-api-ref#codepushoptions) .
For iOS part, you need to add some lines to `AppDelegate.m`, and change from where the app gets release JS bundle.
My *sourceURLForBridge* method looks like this.

```objectivec
// AppDelegate.m
    
(NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
    #if DEBUG
        return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
    #else
        return [CodePush bundleURL]; // this is important line
    #endif
}
```

The deployment key goes to info.plist. To make different build types target different deployments, under Build settings you need to add user-defined setting, called `CODEPUSH_KEY`. For debug, I've left it empty, and for release, I've added Production deployment key. With that, inside `info.plist` configure that *CodePushDeploymentKey* is looking at setting defined in Build settings.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1q3yxvq9b2irtvfv21x0.png)

```html
// info.plist

...
<key>CodePushDeploymentKey</key>
<string>$(CODEPUSH_KEY)</string>
...
```
There is a part for [HTTP exception domains configuration](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-get-started#http-exception-domains-configuration-ios) for iOS apps, and code signing setup for [Android](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-get-started#code-signing-setup-android) and [iOS](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-get-started#code-signing-setup-ios), but for trying out purposes I did not setup it, but it is definitely something to look up and setup.

Now we go to Javascript part of the setup. The first thing we do is that we wrap our App entry to Code Push higher order component (HOC), and add Code Push options

```javascript
import codePush from "react-native-code-push";
    
let codePushOptions = { 
    checkFrequency: codePush.CheckFrequency.ON_APP_RESUME, 
    updateDialog: { appendReleaseDescription: true }
};
    
class MyApp extends Component {
        ..
        ..
}
    
export default codePush(codePushOptions)(MyApp);
```
You can check the list of `codePushOptions` [here](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-api-ref#codepushoptions) . The option `updateDialog: { appendReleaseDescription: true }` means that the user gets update dialog when an update is available.

And that is all the setup needed. There are many options and things to look after in every step of the setup so far, but this setup so far is good enough to get you started. 

You can find a lot of valuable information on [React Native Client SDK guide](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/rn-overview)


## Releasing of the Code Push updates

Releasing of updates is done only through Terminal, and to access App Center the from terminal you need to install [App Center CLI](https://github.com/microsoft/appcenter-cli) . For more details on App Center CLI and how to set it up check [the guide](https://docs.microsoft.com/en-gb/appcenter/distribution/codepush/cli)
To release an update of current code you run 

`appcenter codepush release-react -a <your username>/<your app name> -d <your deployment>`

This will create a new React Native bundle, and upload it to Code Push. 
Next time you launch the release version of the app, you will get a prompt that the update is available.

There is an App Center CLI option to pass a token as an argument, so you can publish new build without logging in to App Center. This is very helpful for DevOps when you want to automatically publish an update when you git push on the production branch.

## Stores guide

Even though this is an "abstract" way to apply updates across devices, Android does not have a specific guideline for this, but Apple does (as usual). You can find more info in the [Store guideline compliance](https://github.com/Microsoft/react-native-code-push#store-guideline-compliance)

## Example apps

I've created a simple TODO app with Code Push implemented, you can check it out here on [github.com/BugBlaster5000/todocp](https://github.com/masimo-orbanic-llds/todocp). It's a simple app that has navigation, Redux for state management and Async storage.