工程指南

在雲端 Mac 建立 Mac Catalyst 雙目標建置門檻

在雲端 Mac 建立 Mac Catalyst 雙目標建置門檻

原本僅支援 iPhone 與 iPad 的專案啟用 Mac Catalyst 後,最常見的誤判是「既然 iOS 能編譯,Mac 端應該也能通過」。但實際進入雲端 Mac 的自動化工作後,問題通常集中在相依套件的平台宣告、資源歸屬、條件式編譯,以及快取交叉共用。可靠的做法不是在既有指令後方多加一個 destination,而是將兩個平台視為共用原始碼、各自獨立驗收的建置產品。

先確認專案確實支援雙目標

先在 Xcode 的目標設定中啟用 Mac Catalyst,再確認專案檔案的相應變更已提交。不要只查看圖形介面中的勾選狀態;自動化工作應從建置設定讀取最終值:

xcodebuild \
  -project DemoApp.xcodeproj \
  -scheme DemoApp \
  -showBuildSettings |
grep -E 'SUPPORTS_MACCATALYST|PRODUCT_BUNDLE_IDENTIFIER|SDKROOT'

SUPPORTS_MACCATALYST 應為 YES。如果專案使用工作區,請將 -project 改為 -workspace。接著執行 xcodebuild -showdestinations,確認共用 scheme 同時提供 iOS Simulator 與 Mac Catalyst destination。若命令列找不到 scheme,應先確認 scheme 是否已設為 Shared,而不是反覆修改 destination 字串。

此外,也要逐一檢查 Swift Package、二進位相依項目及內部模組支援的平台範圍。套件能在 iOS 上編譯,不代表它已宣告支援 Mac Catalyst。對於封閉原始碼的二進位相依項目,應確認 XCFramework 是否包含 Catalyst 對應的切片;若缺少切片,問題可能直到連結階段才會出現。

隔離快取與結果目錄

iOS 與 Catalyst 會產生不同的模組快取、中間物件及連結產物。若持續整合共用同一個 DerivedData,失敗情況可能隨工作執行順序而改變,清理後又暫時消失。建議依平台設定固定目錄,並一併封存結果套件:

set -euo pipefail

xcodebuild \
  -workspace DemoApp.xcworkspace \
  -scheme DemoApp \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath build/derived-ios \
  -resultBundlePath build/results-ios.xcresult \
  CODE_SIGNING_ALLOWED=NO \
  build-for-testing

xcodebuild \
  -workspace DemoApp.xcworkspace \
  -scheme DemoApp \
  -destination 'platform=macOS,variant=Mac Catalyst,arch=arm64' \
  -derivedDataPath build/derived-catalyst \
  -resultBundlePath build/results-catalyst.xcresult \
  CODE_SIGNING_ALLOWED=NO \
  build-for-testing

此處先停用簽署,是為了讓門檻專注檢查原始碼、相依項目與連結是否成立。封存與交付簽署應安排在後續工作中,避免將平台相容性錯誤與簽署設定錯誤混在同一份日誌裡。

不要將刪除整個 ~/Library/Developer/Xcode/DerivedData 視為預設修復方式。精準刪除目前工作的目錄,既不會干擾同一台機器上的其他工作,也能保留失敗現場。

集中處理平台差異,而不是散落判斷

Catalyst 的介面生命週期、選單、視窗行為及部分系統能力與 iOS 不同。條件式編譯應集中於適配層,不要讓 targetEnvironment(macCatalyst) 散落在業務邏輯中。

enum PlatformLayout {
    static var usesDesktopNavigation: Bool {
        #if targetEnvironment(macCatalyst)
        return true
        #else
        return false
        #endif
    }
}

平台專屬 API 還必須搭配可用性檢查。條件式編譯只能指出目前的編譯目標,不代表執行環境的系統版本一定提供該 API。應先為共用協定定義穩定介面,再由 iOS 與 Catalyst 分別實作,讓單元測試能直接驗證兩端行為。

資源也要分別核對。字型、隱私權資訊清單、在地化資源及資料模型,都可能因為漏選 Target Membership 而只進入其中一個產品。建置完成後可檢查產物目錄,確認關鍵檔案確實存在,而不是等到執行階段出現空白介面才發現問題。

檢查項目 iOS Mac Catalyst
相依項目可連結 模擬器切片可用 Catalyst 切片可用
資源歸屬 App 套件內可見 Catalyst App 套件內可見
條件式編譯 行動端分支 桌面適配分支
結果證據 獨立 xcresult 獨立 xcresult

將失敗拆分成可定位的階段

雙目標工作不應只有一個籠統的「建置失敗」。建議依序拆分為相依項目解析、無簽署編譯、單元測試、封存及交付驗收。前一階段通過後才進入下一階段,日誌也應依平台命名。

編譯失敗時先檢查四類訊號

遇到 No such module 時,先檢查相依項目的平台宣告及建置目標;遇到架構不符時,應檢查二進位切片,不要直接新增全域排除架構;遇到 unavailable API 時,回到適配層補上條件式編譯與可用性判斷;遇到資源重複或缺漏時,則檢查 Copy Bundle Resources 與 Target Membership。

建置腳本也可能預設了特定平台。例如,腳本將 SDK 寫死為 iphoneos,Catalyst 工作便會讀取錯誤路徑。腳本應使用 Xcode 注入的 PLATFORM_NAMESDKROOTTARGET_BUILD_DIR,並在遇到未知平台時立即結束。

建立合併前檢查清單

最低限度的門檻應確保兩個目標都能從乾淨目錄完成相依項目解析與無簽署編譯,並分別儲存 .xcresult。核心模組的單元測試應涵蓋平台適配介面;涉及視窗、選單或拖放的行為,則另行安排 Catalyst 專屬測試。

提交前可依下列順序複核:

  • scheme 已共用,命令列可以列出兩個 destination;
  • iOS 與 Catalyst 使用獨立的 DerivedData;
  • 相依項目明確支援兩個平台,且二進位切片完整;
  • 平台判斷集中在適配層,共用邏輯沒有重複實作;
  • 關鍵資源同時進入兩個產品;
  • 失敗日誌、結果套件及建置設定快照依平台封存;
  • 編譯門檻與封存簽署工作分開執行。

在 G-Mini 或其他遠端建置環境中首次導入時,先連續執行兩次,再對調兩個目標的執行順序。若結果會隨順序改變,應優先檢查共用快取、腳本暫存目錄,以及未清除的產生檔案。雙目標流程穩定的標誌,不是偶爾兩端同時通過,而是從乾淨工作區開始,無論採用何種執行順序都能得到一致結果。

常見問題

iOS 與 Mac Catalyst 可以共用 DerivedData 目錄嗎?

本機短暫測試可以,但持續整合建議分開。獨立目錄可避免中間產物與索引狀態互相污染,也更容易分別清理與追查。

雙目標門檻一開始就要跑完整測試嗎?

不必。先讓兩個目標通過免簽章編譯與核心單元測試,再逐步加入介面測試、封存與簽章驗收。

Mac Catalyst 建置失敗時應先檢查什麼?

先確認目標已啟用 Catalyst、相依套件支援該平台、條件編譯分支完整,以及資源與建置階段的目標歸屬正確。

專屬實體節點

為下一個建置佇列準備雲端 Mac

比較兩種 M4 配置與五個節點,依實際工作週期選擇日租、週租、月租或季租。

選擇雲端 Mac 方案