在 Flutter 實現 Shorebird Code Push,即時更新線上產品

Flutter over the air updates

Yii Chen
Flutter Taipei
22 min readApr 10, 2024

--

什麼是 Code Push 呢?它可以稱為 Over The Air updates (OTA),讓開發人員能夠快速地將最新版本推送到線上運行的應用程式。

只要身為 Mobile 開發者,不管是原生還是跨平台開發,都會遇到產品的審核障礙。即便是微小的功能更新、Bug 修正,都需要從重新提交一版應用程式到商店,給官方人員進行審核。在這過程中我們不會知道耗時多久、詳細的檢查步驟,或是有哪些新的通過條件,只能等待 1 天甚至好幾天才能得知結果。大家都希望 Mobile App 能像 Web 一樣,更新即用,快速解決問題,提供給用戶最好的體驗。

而 Shorebird 的出現就是為了解決這個狀況。團隊在去年推出 Code Push,可讓開發人員為產品、應用程式進行 OTA(Over The Air) 更新。這意味著可將新的 APP 版本程式碼直接推送到使用者的手機設備,不需經過標準且繁複的商店審核流程。此外,所有的操作都是透過 Shorebird CLI 也就代表我們能輕鬆整合 Codemagic 等 CICD 工具,簡化部署更新的流程,便利性很高。

對於一般終端用戶來說,因為 Code Push 的更新方法,在即時更新 APP 後,用戶始終可以訪問最新版本的功能,而無需透過 Play Store 和 Apple Store 手動更新。

名稱由來

Flutter 的第一個辦公室位於 Mountain View 的 Shorebird Way

https://twitter.com/_eseidel/status/1773178471189037535

服務介紹

  • 身份驗證:任何人在使用 Shorebard 之前,必須先進行身份驗證。透過常見的 OAuth 驗證進行,以確保只有正常且被驗證的用戶才能存取
  • 角色權限:通常,開發團隊內有不同的職位,例如開發人員和主管。我們可以定義誰有權能建立補丁,誰可以進行部署相關操作
  • 即時更新:幾分鐘即可更新終端用戶的 APP,允許任何的 Dart Code 改動,確保大家能持續存取最新的功能和優化
  • 輕鬆修復:快速解決應用問題,不需要承擔多餘的風險和壓力

平台信任

Shorebard 的 Code Push 功能已通過 Apple 和 Google 的嚴格審查和批准,並有 Eric 創辦人與多位知名開發者作為產品保證,團隊成員精實,活躍程度高。同時確保應用程式更新具有最高等級的安全性,使用標準的加密和身份驗證方法來防止未經授權的存取。

更新過程

預設情況下,Shorebird 在 APP 啟動時會在背景檢查並安裝新補丁。透過 Background isolate 處理,以確保不會影響應用程式的啟動速度。而本次安裝的補丁可在下次啟動應用程式時使用。

官方連結

Shorebird Setup

安裝 Shorebird CLI,開啟 Terminal 輸入指令即可

# macOS
curl --proto '=https' --tlsv1.2 <https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh> -sSf | bash

安裝完成後,重開 Terminal,輸入指令。點擊登入連結進行驗證,內容就是一般的 Google oAuth 登入

shorebird login

此時信箱也會收到 Shorebird 歡迎信件。

Shorebird Initialization

開啟 Flutter 專案,添加配置文件,在專案的根目錄下使用,執行以下指令:

shorebird init

使用參數 —-force 可以重新創建配置檔案,並直接覆蓋原有的資料。

Shorebird 會驗證專案擁有的 flavor 環境,並列出所有做的事情,包括:shorebird.yaml 檔案的創建、新增 shorebird.yaml asset 描述。

shorebird.yaml 檔案裡的 app_id 是 Shorebird 識別應用使用,裡面的資訊都不需要保密。

到 Shorebird 官網開啟 Console,查看專案有的環境設定。

Shorebird Release

創建專案的 release 版本,並將它上傳到 Shorebird Server,同時會生成相對應平台的 artifacts 檔案,有了 Shorebird 就能使用 shorebird release 命令取代 flutter build

目標有四種:

  1. aar → 上傳 Android Library
  2. android → 上傳 Android App (aab, apk)
  3. ios → 上傳 iOS App
  4. ios-framework → 上傳 iOS framework
shorebird release <target>

# Android
shorebird release android --artifact apk

可附帶參數:

  • —-target → 專案的主程式檔案,預設為 main.dart,當然也可以指定 flavor,例如:lib/main_dev.dart
  • —-flavor → 要進行的 flavor 環境
  • —-artifact → Android 生成的檔案,預設為 aab,也可以改成 apk

shorebird release 的作用就像是 flutter build,可以接受原有的任何參數。記得前面要添加兩個 -- 分隔

Android Release

shorebird release android -- --dart-define="key=xxxxxx"

這時候本地會生成出新的 artifact file,如果是 aab,log 會提醒你可以將此檔案上傳到 Play Store; 如果是 apk 你就可以直接安裝到模擬器或實體設備。同時雲端也會有 release 記錄。

如果這時候再執行一次 create release 指令,會出現錯誤訊息,Please bump your version number and try again. ,因為版本相同。每一個 release 代表一次新的改動,所以記得修改 pubspec.yaml 的 flutter version,或將雲端上的 release 記錄刪除,才會發布成功!

iOS Release

首先使用 shorebird release 建置專案的 iOS artifact, 通常是 .ipa 檔案

shorebird release ios --flavor dev --target ./lib/main_dev.dart

接下來就是部署到 Apple Store 的流程,這裡以手動示範,所以從 XCode 上處理,提供幾個步驟:

Step 1. 在 release 發布到 Shorebird Server 之後,我們可以找到 Runner.xcarchive 檔案,雙擊打開

Step 2. 就是正常的 Archives 流程,選擇 Distribute App

Step 3. 正常都是走 TestFlight & App Store 選項,但因為 Shorebird 需要不同的設定,所以選擇 Custom

Step 4. 正常點擊 App Store Connect

Step 5. 這步驟最重要,需要將 Manage version and build number 取消,不用更新版本資訊

Step 6. 接著選擇正確的開發憑證跟 Profile 就能上傳到 Apple Store了。開發者也能透過 TestFlight 進行後續的測試 (包含補丁更新)

Remove Release

可以刪除原本發布到雲端的 release 版本,但記得此操作是不可逆的

shorebird releases delete

#flavor
shorebird releases delete --flavor dev

Shorebird Preview

在發佈 Shorebird release 後,可以使用命令在模擬器和裝置上預覽新的 release 版本。下載 release 版本的 artifacts 檔案,在設備上安裝應用程序,然後啟動應用程式。通常是在開發和 CI/CD 在段使用。

shorebird preview

Shorebird Patch

發布當前 release 版本的 patch 補丁,針對使用此版本的使用者進行更新。當開發者發布補丁後,使用者的應用就能進行下載,接著再下一次開啟時就能直接運行新的 Dart code。

Patch 補丁,不會更改當前應用程式的版本號,它應用於現有版本,而不是新版本。

補丁更新的流程:

  1. 建立更新的 artifact
  2. 下載對應的 release artifact
  3. 檢查新版和舊版的差異,生成補丁 artifact
  4. 將補丁上傳到 Shorebird Server
  5. 將補丁升級到 stable channel
shorebird patch android

shorebird patch ios

# args
shorebird patch android --flavor dev --target ./lib/main_dev.dart -- --dart-define="key=xxxxxx"

Flutter Development

首先在專案新增 Shorebird 開發套件 shorebird_code_push

flutter pub add shorebird_code_push

套件的主要 API 包括:

  • 檢查裝置是否支援 Shorebird
  • 取得目前安裝的 patch 補丁版本
  • 取得下一個補丁版本
  • 檢查是否有新補丁可以下載
  • 下載新補丁

💡檢查是否有新補丁可以安裝

shorebirdCodePush.isNewPatchAvailableForDownload()

💡下載新補丁

shorebirdCodePush.downloadUpdateIfAvailable()

當我們在應用的初始階段下載完新補丁後,需要進行重啟才會有效,才能執行新版的 Dart code。所以建議這時候能提醒使用者更新完成,會執行應用重啟,盡量讓使用者手動點擊後再進行動作,才不會突然重啟而造成體驗不佳。

可以參考官方提供的範例,主要我們需要實作的有幾個步驟:

  1. 檢查是否有新補丁可以下載
  2. 有新補丁,提醒使用者可以更新
  3. 使用者主動執行更新
  4. 顯示更新中的狀態訊息,讓使用者得知當前在做什麼
  5. 下載新的補丁
  6. 顯示更新完成的狀態訊息,讓使用者執行 APP 重啟,使用新功能
  7. 可以透過 restart_app 套件的協助,進行重啟動作
final isUpdateAvailable =
await _shorebirdCodePush.isNewPatchAvailableForDownload();
if (!mounted) return;
setState(() {
_isCheckingForUpdate = false;
});
if (isUpdateAvailable) {
_showUpdateAvailableBanner();
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('No update available'),
),
);
}
}
void _showDownloadingBanner() {
ScaffoldMessenger.of(context).showMaterialBanner(
const MaterialBanner(
content: Text('Downloading...'),
actions: [
SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
],
),
);
}
void _showUpdateAvailableBanner() {
ScaffoldMessenger.of(context).showMaterialBanner(
MaterialBanner(
content: const Text('Update available'),
actions: [
TextButton(
onPressed: () async {
ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
await ㄎ();
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
},
child: const Text('Download'),
),
],
),
);
}
void _showRestartBanner() {
ScaffoldMessenger.of(context).showMaterialBanner(
const MaterialBanner(
content: Text('A new patch is ready!'),
actions: [
TextButton(
// Restart the app for the new patch to take effect.
onPressed: Restart.restartApp,
child: Text('Restart app'),
),
],
),
);
}
Future<void> _downloadUpdate() async {
_showDownloadingBanner();
await _shorebirdCodePush.downloadUpdateIfAvailable(),
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
_showRestartBanner();
}

記得!預設的主動更新關閉,設置 shorebird.yamlauto_update 為 false。

為什麼會需要手動更新或是程式碼控制?有幾個好處:

  1. 控制補丁的安裝(例如:一次僅更新某些帳戶、特定權限的用戶、AB Testing,以減少伺服器負載或降低推出風險)
  2. 尊重用戶,給予用戶安全性的確認,讓用戶可以主動更新

Android Setting

Shorebird Code Push 需要網路權限,為了程式能夠與 Shorebird 伺服器通訊,用戶也才能下載補丁到本地更新。需要修改 AndroidManifest.xml檔案,

<manifest>
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>

執行 shorebird init 時如果權限不存在,則會自動新增權限

Example

Android

示範使用 Dart 程式碼觸發更新。首先以對前版本進行 Shorebird release,此版本正常會是發布到 Play Store 的版本。針對指定環境使用指令,以 Android 進行示範,所以這裡選擇 apk 檔案

shorebird release android --flavor dev --target lib/main_dev.dart --artifact apk

找到 Android apk 檔案,將它安裝到 Android 手機上測試

在手機上安裝 apk 檔案,這裡可模擬成從商店下載。以下為 APP 的初始畫面,而這時候如果發現畫面或是功能上的 bug,就需要緊急透過 Shorebird 發布補丁,修正使用者遇到的問題。

此範例將按鈕的文字與背景顏色做調整,更改了一些程式碼,並將新版發佈出去,使用 shorebird patch 指令。

shorebird patch android --flavor dev --target lib/main_dev.dart

檢查在 Shorebird Cloud 的 release 版本,如果有 release,就將它下載後結合新的程式碼

前置程序完成後會詢問是否要創建補丁,並將生成出來的 artifacts 都上傳到 Shorebird Cloud,這時候終端用戶就會偵測到新補丁了。

Patches 頁面會顯示部署的所有資訊,Patch #2 就是剛剛創建的補丁

iOS

iOS 版本要發布還是比較複雜一點,首先還是使用 shorebird release 指令建置 shorebird artifact,接著將它上傳到 Apple Store。

shorebird release ios --flavor prod --target lib/main_prod.dart

此範例跟 Android 一樣,都是將按鈕的文字與背景顏色做調整,更改了程式碼,並將新版發佈出去,使用 shorebird patch 指令 (這裡需確保補丁的 Flutter 版本跟 Shorebird Release 的版本相同)

shorebird patch ios --flavor prod --target ./lib/main_prod.dart

Demo

  1. 先透過 apk 或是 TestFlight 安裝 release 版本的 APP,然後打開顯示初版的樣子
  2. 更改 Dart 程式碼,在 APP 初始頁面的按鈕配置,包含文字以及背景顏色
  3. 根據相同版本發布 Shorebird Patch
  4. 使用者重啟 APP 時,檢查到新補丁並下載,完成後重啟 APP
  5. 打開後就是新版補丁的 APP,使用新的 Dart code
Android
iOS

當有使用者下載補丁後,團隊可從後台的 Insights 分頁瀏覽目前的使用狀況

Check and Update Timing

  1. 在預設為主動更新的情況下,可以透過推播通知來提醒使用者,當點擊通知開啟 APP 後,因為一樣會觸發 Flutter 引擎的啟動,所以 Shorebird 會執行補丁下載並更新 APP
  2. 在手動更新和程式碼更新的情況下,可以在應用啟動時,或是運行時定期地檢查並下載補丁。當有了新版補丁,我們可以透過 Dialog、Snackbar、Toast 等等樣式來告知使用者,讓他們點擊後重啟 APP 進行更新

Q / A

Q1:Patch 補丁發布後是更新哪一個 release 版本?

  • 目前補丁都是針對最新的 release 版本,所以需要用戶先安裝最新的 release,接下來才能透過補丁進行 Code Push 更新
  • 針對舊版本用戶,可以透過應用內的強制更新機制,引導用戶先下載商店的最新版本,也就是 Shorebird release 版本。這樣後續才能透過 patch 更新應用

Q2:Shorebird 管理我們的 release 版本,會不會有風險,官方直接對客戶產品進行 Code Push?

  • 不會,官方目前保證不會看到也沒有儲存大家的專案程式碼。如果沒有原始程式碼的存取權限,就不可能實現 “Push to Deploy”

Community

加入 Shorebard 的 Discord 社群,了解最新動態、團隊進度,還有開發想法,都可以在上面跟大家互動:

Waiting to be solved

目前還不支援 asset 資源檔案的替換、更新 (目前 v1.0 stable 釋出了,應該會開始處理)

Reference

Other Articles

--

--

Yii Chen
Flutter Taipei

Flutter Lover || Organizer FlutterTaipei || Writer, Speaker || wanna make Flutter strong in Taiwan. https://linktr.ee/yiichenhi