React NativeでCodepushを導入する（AppCenter）

# Codepushについて
React Nativeで開発をしている場合、ネイティブコード（ios/ android/配下）以外のJavaScriptの部分は審査が通った後にCodepushで自由に変更ができます。
ReactNativeのCodepushのデファクトはMicrosoft AppCenterが提供しているものです。

https://github.com/Microsoft/react-native-code-push

使用するためにはAppCenterに登録する必要があります。
http://appcenter.ms/

公式ドキュメント （React Native）
https://docs.microsoft.com/en-us/appcenter/sdk/getting-started/react-native

# 導入方法（iOS）
### yarnでインストール

```bash
# CLIをインストール
yarn global add appcenter-cli
yarn add appcenter appcenter-analytics appcenter-crashes react-native-code-push --exact 
# npm install appcenter appcenter-analytics appcenter-crashes react-native-code-push --save-exact

react-native link # linkが必要なReactNativeのバージョンの場合
```

### Info.plistの変更
AppCenterのダッシュボードからDistributes > CodePushへ進み、右上のレンチアイコンをクリックすると各環境のキーを取得できます。
CFBundleVersionとcodepush.appcenter.msへのアクセスを許可するようにInfo.plistを変更します。

```xml
    ...
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
		<key>CodePushDeploymentKey</key>
	<string>Dy2WRmgfBCaKt_7bOZeKoxxxxxxxxxxxxxxxx</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>
    ...
```

### AppCenter-Config.plist
AppCenter-Config.plistというファイルを作成する（AppCenter用）

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
    <key>AppSecret</key>
    <string>{APP_SECRET_VALUE}</string>
    </dict>
</plist>
```

### AppDelegate.mを修正
sourceURLForBridgeを修正（0.60+）
参考：https://stackoverflow.com/questions/57572651/react-native-0-60-on-ios-code-push-sentry-react-native-navigation-v3-and-app

```objc
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [CodePush bundleURL];
#endif
}
```

#### # React Native 0.59 - 0.59.10の場合：

```objective_c
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  #if DEBUG
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
  #else
    return [CodePush bundleURL];
  #endif
}
```

#### # React Native 0.49 - 0.58の場合：

```objective_c
NSURL *jsCodeLocation;

#ifdef DEBUG
    jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
    jsCodeLocation = [CodePush bundleURL];
#endif
```

#### # React Native 0.48以下の場合：

```objective_c
NSURL *jsCodeLocation;

#ifdef DEBUG
    jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
    jsCodeLocation = [CodePush bundleURL];
#endif
```

### App.tsx(App.js)をラップする

```jsx
const MyApp: React.FunctionComponent<{}> = () => {
  ...
}

// checkFrequency: codepushを確認する頻度
// checkFrequency: 新しいcodepushがある場合に、いつインストールするか（IMMEDIATEなら強制リロードされる）

const options = { checkFrequency: codePush.CheckFrequency.ON_APP_RESUME, installMode: codePush.InstallMode.IMMEDIATE }


const App = codePush(options)(MyApp)
```

# codepushの実行

## 初回設定

```bash
# 初回設定（iOSの例）
appcenter login 
appcenter apps create -d APPNAME-iOS -o iOS -p React-Native
```

## コマンドからcodepush

```bash
# Productionにcodepushする場合（Versionが1.0のアプリが更新される）
appcenter codepush release-react -a username/APPNAME-iOS -d Production -t "1.0"
```

# まとめ
codepushを導入することで、ネイティブコードの修正以外であればAppstoreのアップデートなしで変更ができるようになりました。