關心 iOS/macOS 開發、或是從事包括蘋果平台的跨平台開發的朋友,大概都會注意到今年 CocoaPods 官方宣布進入維護狀態,不再積極開發新功能。同時,Flutter 官方也宣布,接下來蘋果平台的 plugin 開始支援蘋果官方的套件管理工具 Swift Package Management (SPM),慢慢要從 CocoaPods 轉移到 SPM。
這樣的改變自然會衝擊到 Flutter 開發者,這樣的衝擊可以分成兩個部分 — 對 Flutter App 的開發,以及 Flutter Plug-in 開發的部分,整個生態系要轉移到 SPM 上會需要一些時間,但已經是大勢所趨。
要怎麼啟用 Flutter 的SPM 支援,可以參考 Flutter 的官方文件,不過,這兩天試著將手上在維護的一些 Plug-in 升級到 SPM 之後,發現一些官方文件上沒有寫清楚的地方。
將 Flutter App 升級到 SPM
如果您只是開發 App,而沒有開發自己的 Plug-in,那麼升級過程相對來說相當無痛。
基本上只需要按照官方文件升級 Flutter SDK(目前 SPM 支援只在 main 分支中,所以需要切換到 main 分支),然後在終端機中輸入 flutter config — enable-swift-package-manager,就可以打開 SPM 支援。然後,如果某個 Plug-in 已經改用 SPM 管理 iOS 的相依關係,那麼就會用 SPM 載入這個 Plug-in 的 iOS 部分。
Flutter 可以讓你的 App 中混用 SPM 與 CocoaPods,也就是說,如果你的 App 用了許多的 Plug-in,而不同的 Plug-in 之間,有的用 SPM,有的用 CocoaPods,那麼,flutter build 命令會先後載入 SPM 與 CocoaPods 的 iOS Plug-in。
flutter build 命令如果偵測到所有的 Plug-in 都只用 SPM 的話,會建議你移除 CocoaPods 的整合,包括把 ios 或 macos 目錄下的 Podfile 刪掉,然後在 Debug.xcconfig 與 Release.xcconfig 檔案中,刪掉載入 CocfoaPods 的部分…等等。不過,在 ios 或 macos 目錄下的 xcworkspace 不可以刪掉,不然會導致編譯失敗,另外,在這段過渡期間,也不建議太早完全拿掉 CocoaPods 的整合,畢竟說不定下個版本要開發哪個功能,又可能用到某個使用 CocoaPods 的 Plug-in。
官方文件沒講的是,打開 SPM 支援之後,flutter build 命令其實會改動 Xcode project 設定,多加入一個叫做 FlutterGeneratedPluginSwiftPackage 的 Framrework,在做了這個改動之後,拿舊版的 Flutter SDK 編譯,就會編譯失敗。打慨 SPM 支援可說是一條單行道,決定打開之後,就意味著不再繼續使用舊版的 Flutter SDK 了。
將 Flutter Plug-in 升級到 SPM
開發 Flutter 的 iOS 或是 macOS Plug-in 要考慮的就比較多,因為不只是要只支援 SPM,而是需要同時之支援 SPM 與 CocoaPods,讓還在用舊版 Flutter SDK 的 App 開發者不會因為升級 Plug-in 就整個編譯失敗。所以,在 ios/macos 目錄下,就需要同時有 SPM 的 Package.swift,與 CocoaPods 的 podspec 檔案。官方文件中就描述了搬移的過程,大概來說,就是建立一個根 plugin 名稱相同的目錄,在這個目錄下放置 SPM 的檔案(也就是把原本放在 Classes 目錄下的檔案搬過去),最後改一下 podspec,把 source_files 的設定改到新的目錄中。
不過有一些真的很就的 Plug-in,可能還會有 Objective-C 的程式,像是有個 Plug-in 叫做 my_plugin,先用 Objective-C 的 MyPlugin 這個 class 跟 Flutter 連接,然後再去呼叫一個 SwiftMyPlugin 的 Swift class,或是一些過去的奇奇怪怪的狀況…。比較簡單的作法,還是先把原本的 iOS/macOS plugin 搬出來,重新讓工具生成新的 iOS/macOS template 實作。像是
- 先把 ios 搬移成 ios.bak
- 重新呼叫一次 flutter create -t plugin –platforms=ios.android
- 把 ios.bak 下的實作,搬到新的 ios 目錄下
SPM 套件自己會產生一些編譯的產生物,所以在轉換到 SPM 的過程中,也建議在 .gitignore 中,加入 .build、.swiftpm 這些目錄,避免 commit 一些不想要的檔案。
最後講一下跟 CI 有關的部分。Flutter Plug-in 的 CI 通常最起碼要檢查每個支援的平台上、以及想要支援的版本上是不是可以編譯,所以如果用的是 GitHub actions 做 CI,那麼 CI flow 當中大概就會寫成一個 matrix,然後看看 Plug-in 下的 example 能不能編起來。不過,由於升級 SPM 的測試中,我們還會想要測試 example 拿掉了 CocoaPods 整合,是不是還可以正確編譯,所以大概要命外寫一個 CI flow,與原本的 CI flow 區隔開來。
再來,就是像前面講的,打開 SPM 支援之後,flutter build 命令會改動 Xcode project,但我們為了向下相容,所以這些改動,要避免 commit 進去,不然對舊版的 SDK 的 CI 測試就會失敗了。