Codepush-fy your react-native app in 5 minutes! 🚀
Tags: reactnative
Published: 2020-09-23 06:13:17

## What is codepush?

Codepush is awesome tool that makes it possible to bypass store review when updating react-native(or cordova) apps. 

It works like cloud storage of app bundle. After you set everything up and publish your app to the market, on every launch your app can check if there's a new version and updates itself. 

It supports iOS, android and windows apps created with react-native or cordova. 

They say it updates only javascript bundle(when it's RN app), so some updates including assets or native code might cause apps to crash and roll back to older version.

But while using this tool for about four months, no such accident has ever happened. So unless you add native modules or code some out-of-js stuff, you don't have to worry about it.

## How can I use it?

### 1. Install Appcenter-cli

```bash
yarn global add appcenter-cli

or

npm i -g appcenter-cli
```

### 2. Add your app and deployment track to Appcenter.

First, you should login to appcenter using `appcenter-cli`.

```bash
appcenter login # browser will open up. after you login to appcenter, you'll get auth code to type in terminal
```

Now you need to create an app ( not real application, just think this as remote repository of app bundle ) to appcenter.

```bash
# change appname and os as you want
appcenter apps create -d {appname} -o {os}(Android/iOS) -p React-Native
```

In production, I create two apps named `appname-ios, appname-android` each. After this, you can add deployment track of your app. Consider it as a branch of your remote app bundle repository. You can create multiple tracks and control those different update streams seperately.

```bash
appcenter codepush deployment add -a {username/appname} {trackname}
```

When you do this, you'll get a deployment key on your terminal. You will need this, copy it.  If you cleared your terminal by accident, you can re-gain it by this command.

```bash
appcenter codepush deployment list -a {username/appname} -k
```

### 3. Add codepush to your app.

Add codepush dependency on your react-native project. 

```bash
yarn add react-native-code-push
```

#### Setting up iOS

* Add native dependency for iOS

At your root directory of the react-native project.

```bash
cd ios && pod install && cd ..
```

* Edit `AppDelegate.m`.

Import header for codepush.

```objectivec
#import "AppDelegate.h"

#import <CodePush/CodePush.h> // codepush add this line

#import <React/RCTBridge.h>
...
```

And change url that react-native uses to read source bundle.

```objectivec
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  #if DEBUG
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
  #else
    return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];  
    // change the line above to the below
    return [CodePush bundleURL];
  #endif
}
```

* Edit `Info.plist`

Now you should add codepush deployment key and http exception domain to `Info.plist`.

First you should notify your app to which remote repository to access by setting deployment key. Add key `CodePushDeploymentKey` to your `Info.plist` and set value for this. The key that you've copied above. 

```xml
<plist version="1.0">
<dict>
  ...
	<key>CodePushDeploymentKey</key>
	<string>mydeploymentkey</string>
  ...
</plist>

```

And you should allow HTTPS network access to download app bundle.

```xml
<plist version="1.0">
  <dict>
	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
		<key>NSExceptionDomains</key>
		<dict>
			<key>codepush.appcenter.ms</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
		</dict>
	</dict>
  ...
  </dict>
</plist>
```

Finally, your `Info.plist` will be like below and iOS part of your app is ready to codepush.

```xml
<plist version="1.0">
  <dict>
  ...
  <key>CodePushDeploymentKey</key>
  <string>mydeploymentkey</string>
  ...
	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
		<key>NSExceptionDomains</key>
		<dict>
			<key>codepush.appcenter.ms</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
			<key>localhost</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
		</dict>
	</dict>
  ...
  </dict>
</plist>
```

#### Setting up Android 

* Add `codepush.gradle`to `android/app/build.gradle`

```gradle
...
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
...
```

* Edit `MainApplication.java`.

```java
...
// 1. import codepush package
import com.microsoft.codepush.react.CodePush;
public class MainApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        ...
        // 2. override getJSBundleFile 
        @Override
        protected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }
    };
}
```

3. Add deployment key to `strings.xml`

The same key again.

```xml
 <resources>
     <string name="app_name">AppName</string>
     <string moduleConfig="true" name="CodePushDeploymentKey">mydeploymentkey</string>
 </resources>
```

### 4. Actually codepush-fy your app.

You've finished setting your app to access to remote repository on the native side. Now you should tell your react-native code to check version and update itself.

There could be so many scenarios and options, but I'll explain the easiest one.

```jsx
// Consider it's your root Component
import React from 'react'
import codePush from 'react-native-code-push'

const App = () => (
  <AwesomeWrapper>
    <AwesomeContent />
  </AwesomeWrapper>
)

// set options for codepush.
const codePushOptions = {
  checkFrequency: codePush.CheckFrequency.ON_APP_START,
  installMode: codePush.InstallMode.IMMEDIATE,
}

// wrap your root component with codepush and let this to be registered in index.js
export default codepush(codePushOptions)(App)
```

In this case, I set `checkFrequency`, `installMode` options and just wrapped the root component. But you can trigger version check and update manually with `codepush.sync()`. You can read about details at [codepush api](https://github.com/microsoft/react-native-code-push/blob/master/docs/api-js.md).

### 5. Push your code!

All settings are done. You can deploy new app bundle with this command. 

```bash
appcenter codepush release-react -a {username/appname} -d {trackname}
# you should do this at the root directory of your react-native project
# because it read and run commands from your package.json
```