iT邦幫忙

2023 iThome 鐵人賽

DAY 30
0
自我挑戰組

React Native 奇幻之旅系列 第 30

【DAY30】React Native - 使用 CodePush 熱更新應用

  • 分享至 

  • xImage
  •  

前言

有些時候會遇到應用新版本剛上線就發現有bug必須馬上修復的情況,如果是大改動的話重build、重送審也就算了,但如果只是改一行程式碼、改文字、換圖片...等簡單的內容,那麼重新送審就非常浪費時間。

遇到這種情況可以用 CodePush 熱更新應用來解決。

CodePush 是一個雲服務,開發者可以將應用更新上傳到雲端存儲庫,然後 user 在打開應用時會檢查是否有最新的更新可以使用(更新策略可以自行設置),就不需要等待應用重新送審和重新下載的時間。

注意:改原生程式碼(Java, Objective-c)無法透過 CodePush 更新

APP Center 初始化

這部分 iOS 和 Android 方式都一樣所以只以 Android 為例。

先到 APP Center 註冊一個帳號並且新增一個 APP 為 Android - React Native

接著安裝 appcenter-cli 並登入 appcenter

npm install -g appcenter-cli
appcenter login

然後我們需要透過 appcenter apps set-current <ownerName>/<appName> 指令將主要 APP 設為剛剛在 APP Center 新增的 APP:

  • appcenter apps list 確認應用列表
  • appcenter apps get-current 獲取主要 app
  • appcenter apps set-current 設置主要 app
~/R/demo (main)> appcenter apps list
  test/Pokedex
~/R/demo (main)> appcenter apps get-current

~/R/demo (main)> appcenter apps set-current test/Pokedex
~/R/demo (main)> appcenter apps get-current
test/Pokedex

APP Center 更新的流程大概會是從 Staging -> Production,所以預設是會有兩個 deployments,我們需要為這兩個 deployment 建立 KEY:

appcenter codepush deployment add Staging
appcenter codepush deployment add Production

建立完之後,如果要查看所有 deployment 的 key 可以使用下面這個指令:

appcenter codepush deployment list --displayKeys

react-native-code-push

APP Center 的部分設置好之後我們可以用 react-native-code-push 這個官方提供的庫來與 React Native 做連結:

npm install --save react-native-code-push
cd ios && pod install && cd ..

Android 設置

android/settings.gradle 最底部新增以下:

include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')

android/app/src/main/java/com/<PROJECT_NAME>/MainApplication.java 添加以下:

// 1. import CodePush
import com.microsoft.codepush.react.CodePush;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost =
      new DefaultReactNativeHost(this) {
        // ...
        // 2. 新增 getJSBundleFile() 在 mReactNativeHos 裡面
        @Override
        protected String getJSBundleFile() {
          return CodePush.getJSBundleFile();
        }
      };

}

android/gradle.properties 添加 staging 和 release 的 key:

  • appcenter codepush deployment list --displayKeys 可以獲得 key
CODE_PUSH_STAGING_KEY=xxxxxxx
CODE_PUSH_RELEASE_KEY=xxxxxxx

接著在 android/app/build.gradle 將 staging 和 production key 分別寫在 debug, release:

def jscFlavor = 'org.webkit:android-jsc:+'

// 1. 加上這行
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"

// 2. 設置 CodePushDeploymentKey
android {
	buildTypes {
		// ...
		debug {
			resValue "string", "CodePushDeploymentKey", CODE_PUSH_STAGING_KEY
			// ...
		}
		release {
			resValue "string", "CodePushDeploymentKey", CODE_PUSH_RELEASE_KEY
			// ...
		}
	}
}

注意

  1. 網上很多教程會讓你在 strings.xml 添加 CodePushDeploymentKey,其實是不需要的,在 android/app/build.gradle 這裡添加了的話記得將 strings.xml 裡面的 CodePushDeploymentKey 刪除,不然 build 的時候會失敗。
  2. 還有些會讓你在 android/app/build.gradle 加上 apply from "../../node_modules/react-native/react.gradle",這個也不需要,一樣會 build 會失敗。

iOS 設置

記得 Android 跟 iOS 是不同應用,所以 CodePush key 會不一樣,別用錯了。

ios/<project_name>/AppDelegate.mm

  • 新增 #import <CodePush/CodePush.h>
  • 替換 return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];return [CodePush bundleURL];

Xcode - <PROJECT_NAME> - Info - Configuration - Duplicate "Release" Configuration 輸入 Satging

切到 Build Settings - + Add User-Defined Setting

自定義 KEY 名(如:CODEPUSH_KEY),分別輸入 CodePush StagingProduction deployment 的 key:

Info.plist - Information Property List - + CodePushDeploymentKey:$(CODEPUSH_KEY)

使用方法

最簡單的方式是 CodePush-ify 應用的根組件,通常都是 App:

import codePush from "react-native-code-push";

const App = (): JSX.Element => {
  // ...
}

export default codePush(App)

上面的設置都完成之後我們接下來要考慮的部分是:

  • 設置什麼時機(多久)檢查一次更新?(例如,應用啟動、從背景切回前景、點擊按鈕檢查...等)
  • 當有可用更新時該如何下載?(例如:直接自動更新、請用戶自行下載)

在什麼都不設置的情況下 CodePush 會在每次啟動應用的時候檢查更新,如果有可用更新的話它會在下一次重啟應用時自動安裝,用戶可以直接使用到熱更新後的版本而不需要重新下載應用。

若要控制檢查應用更新的時間可以設置 checkFrequecy:

let codePushOptions = {
    checkFrequency: codePush.CheckFrequency.ON_APP_RESUME
}

export default codePush(codePushOptions)(App)

更多 options 請參考官方文檔:https://github.com/microsoft/react-native-code-push/blob/master/docs/api-js.md#codepushoptions

完成以上步驟之後我們先 build 一個最新的 APP 出來安裝並啟動,然後稍微修改一些 UI 部分再進行下面的步驟(為了確認是否有正常熱更新)。

發布應用更新

記得先 appcenter apps get-current 確認一下目前操作的應用是 Android 的還是 iOS 的,別搞混了。或者可以在指令後面加上 -a <APP_NAME> 來指定要操作的是哪個應用。

發布應用更新大致的 workflow 如下:

  1. 使用 appcenter codepush release-react 指令將 CodePush 更新至 Staging
  2. 使用 appcenter codepush promote 指令將測試的版本從 Staging 推到 Production

這邊我們直接使用最基本的指令發布更新:

appcenter codepush release-react

如果需要直接在某個 deployment 發布更新,比如我想直接上 Production,可以加上 -d Production

appcenter codepush release-react -d Production

或者也可以在 APP Center 這邊直接按 Promote 的 button 來將更新推上 Production

一些常見的 flag:

  • [-a <ownerName>/<appName>]:指定應用
  • [-d <deploymentName>]:指定更新要發布到哪個deployment。預設為 Staging
  • [-t|--target-binary-version <targetBinaryVersion>]: 指定要更新的應用的原生版本
  • [-m]:是否強制更新(mandatory),默認為 false
  • [--development]:指定是否生成未壓縮的 js bundle
  • [--description <description>]:更新描述

使用以下指令可以列出最近發出過的更新:

appcenter codepush deployment list

重新開啟 APP 看看剛剛修改的 UI 有沒有更新上去了

注意事項

  1. build android 時 target binary version 會自動讀取 build.gradle 中的 versionName,所以一般情況下不需要特別去設置更新目標版本。
    • 使用 release-react 指令的時候最開始會去 android/app/build.gradletarget binary version
    Detecting android app version:
    Using the target binary version value "1.2.0" from "android/app/build.gradle".
    
  2. Android 使用 ./gradlew assemblerelease 指令打包對應的是 Production deployment,iOS archive 預設也是對應 Production,所以更新時記得指定 deployment 或者將更新 promote 到 Production。
  3. 如果原本安裝的應用版本為 1.0.0,那發布更新時只有指定目標更新(target-binary-version)為 1.0.0 才能更新,或者使用 -t 指定更新的版本範圍,如:appcenter codepush release-react -t ">=1.0.0" -d Production,範圍如何下請參考: https://learn.microsoft.com/zh-tw/appcenter/distribution/codepush/cli#target-binary-version-parameter
  4. 前面我們有設置過主要的 APP(appcenter apps set-current) 所以不需要另外使用 -a 來指定 APP,如果沒指定過的話記得加上 -a <ownerName>/<appName>
  5. 應用更新上去之後至少要等待幾分鐘才會檢查到更新。
  6. 如果版本為 1.0 缺少 patch version 的話,則 patch version 會被視為 0 即 1.0.0

和 Sentry 一起用

Sentry 的配置方式:【DAY25】React Native 使用 Sentry 監控錯誤、異常和性能

CodePush 和 Sentry 可以一起使用,Sentry 官方文檔也有提到集成 CodePush 的方法

codePush(codePushOptions)(Sentry.wrap(App))

// App.tsx
import * as Sentry from '@sentry/react-native'
import CodePush from 'react-native-code-push'

let codePushOptions = {
  checkFrequency : codePush.CheckFrequency.ON_APP_START
};

Sentry.init({
  dsn: Config.SENTRY_DSN,
  release: Config.release,
  dist: Config.dist,
  integrations: [
    new Sentry.ReactNativeTracing({
      routingInstrumentation
    })
  ]
})

function App(): JSX.Element {
  // ...
}

export default codePush(codePushOptions)(Sentry.wrap(App))

更新時透過指定 --sourcemap-output 和 --output-dir ./build 來輸出套件和來源映射。這將輸出到 ./build/CodePush 資料夾。

appcenter codepush release-react -d {DEPLOYMENT} --sourcemap-output --output-dir ./build

將輸出的套件和來源映射上傳到 Sentry

export SENTRY_PROPERTIES=./ios/sentry.properties
sentry-cli react-native appcenter {OWNER_NAME}/{APP_NAME} ios ./build/CodePush --deployment {DEPLOYMENT} --dist {DIST}

參考資料


上一篇
【DAY29】打包 iOS APP 發布到 TestFlight 測試與上架 App Store
下一篇
【DAY31】完賽啦
系列文
React Native 奇幻之旅31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言