Xcode 的 project.pbxproj 檔案採用文字格式儲存專案結構,但 Xcode 在新增檔案或修改設定時,不保證項目的插入順序一致。多人協作時,即使修改不同的檔案或 target,也可能因為項目順序差異而產生 merge conflict。這些衝突往往與實際變更無關,純粹是格式問題。

我的解決方案

project.pbxproj 中的各個區塊按固定規則排序,確保相同內容產生相同的檔案結構。配合 git pre-commit hook,每次提交前自動排序,團隊成員的專案檔就能維持一致的順序,大幅降低無意義的衝突。

我開發了一個腳本工具來執行這個任務,也已經在多個專案上跑了好幾年,GitHub repo 放在這裡:https://github.com/chiahsien/sort-Xcode-project-file

雖然目前推崇使用 Swift Package Manager 進行模組化,Xcode 16 也引入了 buildable folders 功能來減少專案檔變更,甚至也有 Tuist 或 Xcode Gen 這類的工具來生成專案檔,但這些新技術主要針對新專案或願意大幅重構的專案。對於已經開發多年、結構複雜的舊專案,貿然改用 SPM 模組化或轉換成 folder references 風險過高。此時,這個排序工具仍是最務實的選擇,能以最小成本解決 merge conflict 問題。

排序範圍

此工具排序以下區塊:

Array 結構

  • children — group 內的檔案與 subgroup
  • buildConfigurations — build configuration 列表
  • targets — 專案 target 列表
  • packageProductDependencies — Swift Package product dependency
  • packageReferences — Swift Package reference
  • files — build phase 的檔案列表

Section 區塊

  • PBXFileReference — 檔案 reference
  • PBXBuildFile — build file
  • PBXGroup — group 定義
  • PBXVariantGroup — 本地化檔案 group
  • PBXReferenceProxy — 外部專案 reference
  • PBXContainerItemProxy — container proxy
  • PBXTargetDependency — target dependency
  • XCBuildConfiguration — build configuration
  • XCConfigurationList — configuration list

安全性

這些區塊是宣告性內容,Xcode 透過 24 字元的十六進位 ID 參照物件,而非依賴位置。排序不影響建置行為或專案結構,僅改變檔案內的呈現順序。

不排序的區塊:PBXFrameworksBuildPhase 的 framework 連結順序會影響符號解析,因此保持原始順序。buildPhases array 決定編譯順序,同樣不進行排序。

警告:
雖然這個工具已經在多個不同專案執行很長一段時間了,我還是強烈建議在修改之前先做好備份,才不會出現難以挽回的錯誤!

與原版的差異

本版本是 WebKit 專案的 fork,新增以下功能:

  • Natural sorting:數字部分按數值比較,file2.m 排在 file10.m 前面,符合人類直覺
  • Case-insensitive 選項:提供 --case-insensitive 參數支援不分大小寫排序,預設仍為 case-sensitive 以保持原始行為
  • 更多區塊排序:原版僅排序部分 section,本版涵蓋所有安全的宣告性區塊
  • 自動去除重複:移除重複的項目 reference
  • 擴充排序範圍:包含 mainGroup 的頂層目錄與檔案、targets 列表、packageProductDependenciespackageReferences

使用方法

基本呼叫

perl sort-Xcode-project-file.pl path/to/Project.xcodeproj

腳本會自動找到 project.pbxproj 並就地排序。

選項

# 使用 case-insensitive sorting
perl sort-Xcode-project-file.pl --case-insensitive Project.xcodeproj

# 抑制 warning 訊息
perl sort-Xcode-project-file.pl --no-warnings Project.xcodeproj

# 顯示說明
perl sort-Xcode-project-file.pl --help

Git Hook 整合

請參考 repo 的 README