在iOS專案中依賴Flutter Module-③本地podspec中轉依賴遠端Flutter編譯產物

JK_溪楓發表於2021-08-17

在上一篇# 在iOS專案中依賴Flutter Module-②遠端依賴Git資源實現了將Flutter編譯產物釋出到遠端Git倉庫以及iOS端遠端依賴Flutter編譯產物的方案。但是這個方案有一個很大的問題,就是Flutter.xcframework檔案太大,新版Flutter編譯出來接近480M,如果隨其它產物一起打上版本tag,每次釋出到Git、從Git上拉取新版本都將耗費不少時間。由於我們並不需要改動Flutter Engine,所以每次編譯匯出的Flutter.xcframework都不會變,除非Flutter版本更新了。而App.xcframework和其它第三方外掛庫跟我們編寫的Dart程式碼是關聯的,每次編譯都可能會變更。所以正常情況下,每次釋出新版本產物,只需要釋出除Flutter.xcframework以外的產物即可。

回顧# 在iOS專案中依賴Flutter Module-①本地依賴 - 章節3,可以選擇編譯匯出Flutter.podspecFlutter.podspec依賴了遠端伺服器上的Flutter.xcframework壓縮檔案,並且支援CDN加速,壓縮後不到原檔案的一半大小,首次下載下來也比拉取Git快。Flutter.podspec的版本號對應本機上的Flutter版本號,基本不會變化,CocoaPods根據版本號快取資源,就不會反覆下載更新。相比將Flutter.xcframework放到Git上,通過本地的Flutter.podspec中轉依賴Flutter.xcframework.zip,不僅減少Git伺服器的儲存壓力,還能減少不必要的時間損耗,能明顯縮短整個流程的時間。

圍繞本地podspec中轉依賴遠端Flutter,編譯產物,我嘗試了幾種方案,最終我選擇了本地podspec中轉依賴檔案伺服器上的Flutter.xcframework.zip + 本地podspec中轉依賴Git上的其它.xcframework,充分發揮遠端zip檔案的下載優勢和Git版本管理的優勢。

也可以自行搭建檔案伺服器,將其它的xcframework壓縮成zip檔案上傳到檔案伺服器,像Flutter.podspec一樣通過本地podspec中轉依賴zip檔案。但是不好做版本控制和版本回滾,檔案伺服器上面也會不斷累計舊版本的編譯產物,對於APP端開發者而言搭檔案伺服器也有點學習成本,整體而言我覺得沒`遠端zip & git混合依賴`的方案好用,後面就不做介紹了,如果感興趣,就進傳送門,可以找我要Node.js搭檔案伺服器的程式碼,網上找也可以。
複製程式碼

如果想看更多的試驗方案,請進傳送門?

準備階段

為了方便測試程式碼,我把ios_module/flutter_module/andriod_module放在了一個工作區目錄下。ios_module就是iOS專案所在目錄,整體目錄結構如下:

some/path/ 
├── andriod_module
│   ├── ...
├── flutter_module
│   ├── ...
├──ios_module
│   ├── ...
複製程式碼

先建立一個Flutter Module,隨便加點flutter程式碼和第三方元件(比如flutter_boost);

cd some/path/ 
flutter create --template module flutter_module
複製程式碼

建立一個臨時目錄flutter_build/Frameworks/,用於存放編譯匯出的產物,用完即刪,至此形成的目錄結構如下;

some/path/ 
├── andriod_module
│   ├── ...
├── flutter_build
│   ├── Frameworks
├── flutter_module
│   ├── README.md
│   ├── build
│   ├── flutter_module.iml
│   ├── flutter_module_android.iml
│   ├── lib
│   ├── pubspec.lock
│   ├── pubspec.yaml
│   └── test
├──ios_module
    ├── FlutterBoostPro
    ├── FlutterBoostPro.xcodeproj
    ├── FlutterBoostPro.xcworkspace
    ├── Podfile
    ├── Podfile.lock
    └── Pods
複製程式碼

建立Git倉庫FlutterModuleSDK.git,用來存放除Flutter.xcframework以外的編譯產物,建好git之後克隆到本機(可以是工作區some/path/下)。

然後建立Git倉庫FlutterModuleSDKPodspec.git,建好git之後克隆到本機(可以是工作區some/path/下),用來存放Flutter.podspecFlutterModuleSDK.podspecFlutter.podspec是編譯匯出的檔案,FlutterModuleSDK.podspec需要我們自行建立,並指定依賴前面建立的FlutterModuleSDK.git,檔案內容如下:

Pod::Spec.new do |s|
  s.name                  = 'FlutterModuleSDK'
  s.version               = '1.0.1'
  s.summary               = 'Flutter Module SDK'
  s.source                = { :git => 'https://a.gitlab.cn/flutter/FlutterModuleSDK.git', :tag => "#{s.version}" }
  s.platform              = :ios, '8.0'
  s.requires_arc          = true
  s.vendored_frameworks   = '*.xcframework'
end
複製程式碼

最後形成的目錄結構如下

井號 # 代表目前不存在,編譯後才會有,先佔個位而已

some/path/ 
├── andriod_module
│   ├── ...
├── flutter_build
│   ├── Frameworks
│   │   ├── # Debug
│   │   ├── # Profile
│   │   ├── # Release
├── flutter_module
│   ├── README.md
│   ├── build
│   ├── flutter_module.iml
│   ├── flutter_module_android.iml
│   ├── lib
│   ├── pubspec.lock
│   ├── pubspec.yaml
│   └── test
├──ios_module
│   ├── FlutterBoostPro
│   ├── FlutterBoostPro.xcodeproj
│   ├── FlutterBoostPro.xcworkspace
│   ├── Podfile
│   ├── Podfile.lock
│   └── Pods
├──FlutterModuleSDK
│   ├── # App.xcframework、FlutterPluginRegistrant.xcframework和其它.xcframework
├──FlutterModuleSDKPodspec
│   ├── FlutterModuleSDK.podspec
│   ├── # Flutter.podspec   
複製程式碼

編譯

先編譯flutter_module,指定匯出目錄是flutter_build/Frameworks/output的引數是相對路徑。編譯失敗的話要麼是Dart程式碼問題,要麼是.ios/Runner.xcworkspace的開發證書沒配置好。

cd flutter_module/
flutter build ios-framework --cocoapods --xcframework --no-universal --output=../flutter_build/Frameworks/
複製程式碼

編譯成功後,flutter_build/Frameworks/目錄下面會新增以下資料夾。

├── flutter_build
│   ├── Frameworks
│   │   ├── Debug
│   │   ├── Profile
│   │   ├── Release
複製程式碼

20210706145922.jpg

處理產物

App.xcframework、FlutterPluginRegistrant.xcframework和其它.xcframework移到FlutterModuleSDK.git本地目錄下;

├──FlutterModuleSDK
│   ├── App.xcframework
│   ├── FlutterPluginRegistrant.xcframework
│   ├── 其它.xcframework,比如flutter_boost.xcframework
複製程式碼

Flutter.podspec移到FlutterModuleSDKPodspec.git本地目錄下(並不需要修改這個檔案,直接用);

├──FlutterModuleSDKPodspec
│   ├── FlutterModuleSDK.podspec
│   ├── Flutter.podspec
複製程式碼

修改FlutterModuleSDK.podspec中的版本號;

釋出產物

提交FlutterModuleSDK.git中的更新,打上版本tag,push到遠端倉庫;

提交FlutterModuleSDKPodspec.git中的更新,push到遠端倉庫;

清空刪除flutter_build/Frameworks/

釋出版本更新通知(釘釘、微信、郵件);

iOS端拉取更新

同步剛剛釋出的*.podspec到iOS開發機上,第一次需要克隆FlutterModuleSDKPodspec.git到本地,後面只需要拉取更新FlutterModuleSDKPodspec.git即可;

在Podfile中新增本地依賴,:podspec指定FlutterModuleSDKPodspec.git中對應的.podspec檔案,並使用相對路徑;

  pod 'Flutter', :podspec => './../FlutterModuleSDKPodspec/Flutter.podspec'
  pod 'FlutterModuleSDK', :podspec => './../FlutterModuleSDKPodspec/FlutterModuleSDK.podspec'
複製程式碼

pod update or install,執行程式碼測試;

儘量在.gitignore檔案中忽略掉編譯期間匯入的.xcframework,不然一併提交到Git會很慢,還明顯增加Git倉庫的檔案大小。
複製程式碼

由於Flutter.podspec的版本號基本不會變,只有第一次下載zip檔案會耗費點時間,CocoaPods快取以後執行更新都會非常快。而FlutterModuleSDK.git裡面的檔案並不大,在內網Git的話每次更新都會比較快。所以相比將Flutter.xcframework放到Git上,通過本地的Flutter.podspec中轉依賴Flutter.xcframework.zip,不僅減少Git伺服器的儲存壓力,還能減少不必要的時間損耗,能明顯縮短整個流程的時間。 另外這種方案也不用iOS同事安裝Flutter開發環境,依賴Flutter側程式碼就跟依賴第三方庫一樣,由於*.xcframework已經編譯過了,編譯iOS專案時不需要再編譯Flutter程式碼,相比本地依賴就要快得多。

但這個方案也有個短板,如果iOS團隊人多的話,要保證所有人在Podfile中填的:podspec相對路徑都是一樣的,也就是要求在每臺開發機上,本地FlutterModuleSDKPodspec.git相對本地ios_module.git的路徑是一致的。如果跟其他人填的相對路徑不一致,提交到Git就會發生衝突。當然也可以禁止大家在克隆git時重新命名本地倉庫名,並且把FlutterModuleSDKPodspec.gitios_module.git在同一個目錄下,只是時間一久,難免會遺忘這個規則,另外還得跟新人強調這個規則。

改良

前面說了要保持相對路徑一致,而FlutterModuleSDKPodspec.gitios_module.git是純人為建立並維護的關聯關係,時間久了容易遺忘這個共識,所以需要更強烈的繫結關係來解決這個隱患。我找到的方案是藉助git submoduleFlutterModuleSDKPodspec.git"內嵌"到ios_module.git中,成為子模組,還能讓FlutterModuleSDKPodspec.git保持獨立的Git倉庫。

關於git submodule的介紹可以看某大佬整理的《Git中submodule的使用》

所以只需要修改iOS端拉取更新流程,在ios_module.git中增加git submodule,解決相對路徑難以保持一致的問題,前面的編譯、釋出流程都不用改,FlutterModuleSDKPodspec.git還是獨立的倉庫。

iOS端拉取更新-改良版

首先在ios_module.git中新增子模組FlutterModuleSDKPodspec.git

  cd some/path/ios_module
  git submodule add 'https://a.gitlab.cn/flutter/FlutterModuleSDKPodspec.git'
複製程式碼

新增子模組後,主要會遇到兩種情況,首先是新同事首次克隆ios_module.git,然後是後續的子模組更新。

  • 如果是新同事首次克隆ios_module.git,可能會遇到2種情況。
    • 1.常規的克隆指令,可能指定分支,但沒有拉取submodule子模組,就需要額外執行指令去遞迴拉取更新子模組。
      git clone -b a_branch https://a.gitlab.cn/ios/ios_module.git 
      git submodule update --init --recursive
    複製程式碼
    • 2.克隆的同時指定遞迴拉取更新子模組,則需要在指令中加上--recurse-submodules
      git clone -b a_branch https://a.gitlab.cn/ios/ios_module.git --recurse-submodules
    複製程式碼
  • FlutterModuleSDKPodspec.git釋出更新後,需要在ios_module.git中更新本地子模組,加上foreach可以幫助我們遍歷所有的子模組。
  git submodule foreach 'git pull origin master'
複製程式碼

如果更新了子模組(包括新增子模組),都會在ios_module.git產生modify,所以還需要額外提交更新到ios_module.git

  git status
  # 下面2步可以在SourceTree之類的客戶端操作,是可以看到submodule的
  git add -A && git commit -m "新增或更新子模組 FlutterModuleSDKPodspec.git"
  git push origin --tags && git push origin master 
複製程式碼

然後在Podfile中新增本地依賴,:podspec指定FlutterModuleSDKPodspec.git中對應的.podspec檔案,並使用子模組的相對路徑

  pod 'Flutter', :podspec => './FlutterModuleSDKPodspec/Flutter.podspec'
  pod 'FlutterModuleSDK', :podspec => './FlutterModuleSDKPodspec/FlutterModuleSDK.podspec'
複製程式碼

pod update or install,執行程式碼測試;

儘量在.gitignore檔案中忽略掉編譯期間匯入的.xcframework,不然一併提交到Git會很慢,還明顯增加Git倉庫的檔案大小。
複製程式碼

綜上所述,改良版就是把更新依賴的FlutterModuleSDKPodspec.git改成了更新子模組,而FlutterModuleSDKPodspec.git以子模組嵌入ios_module.git後路徑是固定的,這樣就能保住每臺電腦上這2個git的相對路徑是始終一致的,不用擔心人為因素導致路徑不一致的問題。

至此就實現了在iOS專案中遠端依賴Flutter Module編譯產物,特別感謝閒魚團隊分享的《Flutter in action》,遠端依賴的思路也是從中獲取的,只是不知道閒魚團隊具體是怎麼實現的,所以花了不少時間在找合適的方案,試了幾種方案,有可行的也有行不通的方案,最後選擇了Git子模組 + 本地podspec中轉遠端zip & git的方案,具體的特點就是下面這些。總體而言,這個方案充分發揮了遠端zip檔案的下載優勢和Git版本管理的優勢,適合大小團隊使用,技術難度低,關鍵能為iOS端節省很多編譯時間,這一點特別實用。

  • 將*.xcframework釋出到FlutterModuleSDK.git中
  • 將*.podspec釋出FlutterModuleSDKPodspec.git中
  • 將FlutterModuleSDKPodspec.git 以子模組的形式內嵌到 ios_module.git中
  • 通過子模組的Flutter.podspec中轉依賴遠端伺服器上的zip檔案
  • 通過子模組的FlutterModuleSDK.podspec中轉依賴遠端Git上的*.xcframework

相關文章