最佳實踐
多年來與不同團隊和項目合作,我們總結出一套最佳實踐,建議在使用 Tuist 和 Xcode 項目時遵循。這些實踐並非強制要求,但可以幫助你以更易於維護和擴展的方式組織項目。
Xcode#
不推薦的模式#
用配置來模擬遠端環境#
許多組織使用構建配置來模擬不同的遠端環境(例如 Debug-Production 或 Release-Canary),但這種方法有一些缺點:
- 不一致性: 如果依賴圖中存在配置不一致的情況,構建系統可能會為某些目標使用錯誤的配置。
- 複雜性: 項目最終可能會擁有一長串難以理解和維護的本地配置和遠端環境。
構建配置旨在體現不同的構建設置,而項目很少需要超過 Debug 和 Release。模擬不同環境的需求可以通過其他方式實現:
- 在 Debug 構建中: 你可以將開發期間應可訪問的所有配置(例如端點)包含在應用中,並在運行時切換。切換可以通過 scheme 啟動環境變量或應用內的 UI 進行。
- 在 Release 構建中: 在發布情況下,你可以只包含與發布構建綁定的配置,並通過使用編譯器指令不包含用於切換配置的運行時邏輯。
雖然 Tuist 支持非標準配置,並且相比原生 Xcode 項目更易於管理,但如果配置在整個依賴圖中不一致,你將會收到警告。這有助於確保構建的可靠性並防止與配置相關的問題。
生成的項目#
可構建文件夾#
Tuist 4.62.0 增加了對 可構建文件夾(Xcode 的同步組)的支持,這是 Xcode 16 引入的一項功能,旨在減少合併衝突。
雖然 Tuist 的通配符模式(例如 Sources/**/*.swift)已經消除了生成項目中的合併衝突,但可構建文件夾提供了額外的好處:
- 自動同步:你的項目結構與文件系統保持同步——添加或刪除文件時無需重新生成
- 對 AI 友好的工作流程:編碼助手和代理可以修改你的代碼庫,而不會觸發項目重新生成
- 更簡單的配置:定義文件夾路徑,而不是管理明確的文件列表
我們建議採用可構建文件夾,而不是傳統的 Target.sources 和 Target.resources 屬性,以獲得更流暢的開發體驗。
let target = Target(
name: "App",
buildableFolders: ["App/Sources", "App/Resources"]
)
let target = Target(
name: "App",
sources: ["App/Sources/**"],
resources: ["App/Resources/**"]
)
依賴項#
在 CI 上強制使用已解析的版本#
在 CI 上安裝 Swift Package Manager 依賴項時,我們建議使用 --force-resolved-versions 標誌以確保確定性構建:
tuist install --force-resolved-versions
此標誌確保使用 Package.resolved 中固定的確切版本來解析依賴項,從而消除由依賴項解析中的非確定性引起的問題。這在 CI 上尤為重要,因為可重複的構建至關重要。
在 GitHub Actions 上緩存依賴項安裝#
對於生成的項目,使用 tuist/install 來安裝 Package.resolved 中固定的依賴項,並在多次運行之間緩存它們。它會在 Tuist Runners 上自動使用 緩存卷,在其他 runner 上使用 GitHub 的緩存。
name: Generate project
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
generate:
runs-on: tuist-macos
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v4
- uses: tuist/install@v1
- run: tuist generate --no-open
對於子目錄中的項目,傳遞相對於 checkout 的路徑:
- uses: tuist/install@v1
with:
path: apps/ios
查看 action 參考 以獲取更多選項。
代理編碼和工作樹#
編碼代理和人類貢獻者越來越多地並行工作:開發人員保持一個工作樹打開以審查他們正在審核的功能,而一個或多個代理在兄弟工作樹中的自己的分支上迭代。一些項目選擇可以使這更加順暢。
使用可構建文件夾進行代理驅動的編輯#
當代理添加或刪除文件時,任何依賴於 Project.swift 中文件列表的內容都需要在項目再次構建之前執行 tuist generate。可構建文件夾將該責任推給 Xcode 的同步組,因此文件樹編輯不再觸發重新生成。
將其與 generated-projects 技能結合使用,這教會代理 Tuist 項目的日常 workflows。
不要按工作樹隔離緩存#
Tuist 遵循 XDG 基礎目錄規範,因此默認情況下,同一台機器上的每個工作樹共享 ~/.cache/tuist 和 ~/.local/state/tuist。這正是你想要的:在一個工作樹中編譯的助手、在另一個工作樹中下載的插件以及由第三個工作樹拉取的模塊緩存二進制文件都是可重用的。
跨工作樹的併發 tuist 調用可以安全地並行運行。避免將 TUIST_XDG_CACHE_HOME 指向工作樹本地的路徑,除非你特別需要隔離;這樣做會迫使每個工作樹重新預熱助手、插件和二進制文件。
保持每個工作樹的 SwiftPM 臨時目錄#
Swift Package Manager 有自己的路徑控制。--scratch-path 是可變的工作狀態,默認為 .build,而 --cache-path 是共享的緩存目錄。當多個工作樹處於活動狀態時,不應將這兩條路徑同等對待。
Tuist 的預設 tuist install 行為已經將 SwiftPM 臨時目錄保留在套件目錄下,因此每個工作樹都有自己的 .build。保持這種隔離。單個共享的 SwiftPM 臨時目錄可能會混合來自不相關分支的 checkout、構建中間文件、插件、宏和鎖定文件。
如果你通過 tuist install -- ... 或通過 Tuist.swift 中的 installOptions 傳遞 SwiftPM 參數,在希望更快獲取依賴項時共享 --cache-path,但讓 --scratch-path 保持未設置或將其指向工作樹本地的目錄:
import ProjectDescription
let tuist = Tuist(
project: .tuist(
installOptions: .options(
passthroughSwiftPackageManagerArguments: [
"--cache-path",
"/Users/Shared/swiftpm/cache/xcode-16-macos",
]
)
)
)
如果緩存路徑需要根據 Xcode、Swift 工具鏈或平台而變化,請使用包裝器或 CI 環境變量。避免將共享的 --scratch-path 提交到倉庫。
如果依賴項狀態僅在一個工作樹中看起來已損壞,請刪除該工作樹的 .build 目錄並再次運行 tuist install。如果共享的 SwiftPM 緩存在多個工作樹之間看起來可疑,請在重新安裝依賴項之前使用 swift package purge-cache。
依賴模塊緩存#
模塊緩存 使並行工作樹變得可行。因為 Tuist 為每個目標計算確定性的 哈希,所以指向共享大部分歷史記錄的分支的兩個工作樹會命中相同的二進制文件。在 CI 上每次提交到 main 時運行 tuist cache;在本地,每個工作樹中的 tuist generate 會從緩存中提取它可以提取的任何內容,並且只構建你的分支實際更改的內容。
如果工作樹在你預期快取命中時回退到源代碼構建,請遵循 哈希調試步驟;非確定性哈希(通常由項目中的絕對路徑引起)是最常見的原因,並且在比較兩個工作樹時會明顯顯示出來。
使用註冊表加速 Swift Package 解析#
每個工作樹獨立解析 Swift 套件。對於具有大型依賴圖的項目,這會快速累積。切換到 Tuist Registry 可以避免重新克隆 git 歷史記錄,並將解析時間從幾分鐘減少到幾秒鐘。
讓代理訪問項目見解#
一旦構建和測試在許多工作樹中運行,代理最有用的事情就是推理 哪些 運行出現了回歸,哪些測試是不穩定的,或者捆綁包大小如何變化。Tuist 通過 MCP 服務器 和 Skills 套件公開這些數據。每個工作流程選擇一個並堅持使用它;將兩者混合用於同一任務往往會導致代理行為不一致。