xcodebuild -resolvePackageDependenciesは、プロジェクトが参照するSwiftパッケージの依存関係を解決する。パッケージの取得とPackage.resolvedの更新だけを実行し、ビルドはしない。

$ xcodebuild -resolvePackageDependencies -project [プロジェクト名].xcodeproj -scheme [スキーム名]
Resolve Package Graph
Fetching from [リポジトリのURL] (cached)
Creating working copy of package ‘[パッケージ名]’
Checking out [バージョン] of package ‘[パッケージ名]’
Resolved source packages:
  [パッケージ名]: [リポジトリのURL] @ [バージョン]
resolved source packages: [パッケージ名]

CIでは、依存関係の解決とビルドを別のステップに分けると、失敗の原因を切り分けやすい。

実行例はXcode 27.0、macOS 27.0.1のもの。

作られるファイル

プロジェクトが依存するパッケージのバージョンは、Package.resolvedに記録される。

[プロジェクト名].xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Package.resolvedには、パッケージごとにバージョンとリビジョンが記録される。

{
  "originHash" : "[ハッシュ値]",
  "pins" : [
    {
      "identity" : "yams",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/jpsim/Yams",
      "state" : {
        "revision" : "[リビジョン]",
        "version" : "6.2.2"
      }
    }
  ],
  "version" : 3
}

取得したパッケージは、DerivedDataのSourcePackagesフォルダに保存される。

$ ls [DerivedDataのパス]/SourcePackages
artifacts
checkouts
repositories
workspace-state.json

-derivedDataPathを指定するときは、-schemeも指定する。-schemeがないと、エラーで終了コード64になる。

xcodebuild: error: The flag -scheme, -testProductsPath, or -xctestrun is required when specifying -derivedDataPath.

-derivedDataPathを指定しないと、~/Library/Developer/Xcode/DerivedDataに保存される。

Package.resolvedのバージョンは維持される

Package.resolvedに記録済みのバージョンが、プロジェクトの要件を満たしているときは、より新しいバージョンがあっても、記録されたバージョンのまま解決される。要件が6.0.0以上のパッケージで、Package.resolvedを6.0.0に書き換えて解決すると、6.0.0が維持された。

$ xcodebuild -resolvePackageDependencies -project [プロジェクト名].xcodeproj -scheme [スキーム名]
Checking out 6.0.0 of package ‘[パッケージ名]’
Resolved source packages:
  [パッケージ名]: [リポジトリのURL] @ 6.0.0

プロジェクトの要件を6.1.0以上に変更すると、記録されたバージョンが要件を満たさなくなる。この状態で解決すると、Package.resolvedが最新のバージョンに更新された。

$ xcodebuild -resolvePackageDependencies -project [プロジェクト名].xcodeproj -scheme [スキーム名]
Updating from [リポジトリのURL]
Checking out 6.2.2 of package ‘[パッケージ名]’
Resolved source packages:
  [パッケージ名]: [リポジトリのURL] @ 6.2.2

Package.resolvedの更新を禁止する

-disableAutomaticPackageResolutionを付けると、Package.resolvedに記録されたバージョン以外には解決しない。-onlyUsePackageVersionsFromResolvedFileも、xcodebuild -helpでは同じ説明である。Package.resolvedの内容が不足していたり古かったりすると、xcodebuildは終了コード74で失敗する。

Package.resolvedがない状態でbuildすると、次のエラーになる。

$ xcodebuild build -project [プロジェクト名].xcodeproj -scheme [スキーム名] -disableAutomaticPackageResolution
xcodebuild: error: Could not resolve package dependencies:
  a resolved file is required when automatic dependency resolution is disabled and should be placed at [プロジェクトのパス]/[プロジェクト名].xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved. Running resolver because the following dependencies were added: ...
$ echo $?
74

プロジェクトの要件を変えて、Package.resolvedが古くなったときも、同じ終了コード74で失敗する。

xcodebuild: error: Could not resolve package dependencies:
  an out-of-date resolved file was detected at [プロジェクトのパス]/[プロジェクト名].xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved, which is not allowed when automatic dependency resolution is disabled; please make sure to update the file to reflect the changes in dependencies. ...

フラグを付けないxcodebuild buildは、Package.resolvedがなくても、自動で依存関係を解決してビルドに成功する。CIでは、Package.resolvedをコミットして、-disableAutomaticPackageResolutionを付けると、リポジトリに記録されたバージョンだけでビルドできる。

パッケージの保存先を指定する

-clonedSourcePackagesDirPathは、取得したパッケージの保存先を指定する。解決とビルドで同じフォルダを指定すると、ビルドでは取得が実行されない。

$ xcodebuild -resolvePackageDependencies -project [プロジェクト名].xcodeproj -scheme [スキーム名] -clonedSourcePackagesDirPath SourcePackages
$ ls SourcePackages
artifacts
checkouts
repositories
workspace-state.json
$ xcodebuild build -project [プロジェクト名].xcodeproj -scheme [スキーム名] -derivedDataPath DerivedData -clonedSourcePackagesDirPath SourcePackages -disableAutomaticPackageResolution
** BUILD SUCCEEDED **

実行例のビルドは、-derivedDataPathが別のフォルダでも、FetchingやChecking outは出力されずに成功した。

パッケージの保存先を、DerivedDataの外のフォルダにすると、CIのキャッシュの対象にして、取得済みのパッケージを再利用できる。

取得に失敗したとき

取得先のリポジトリが存在しないと、終了コード74で失敗する。

$ xcodebuild -resolvePackageDependencies -project [プロジェクト名].xcodeproj -scheme [スキーム名]
...
xcodebuild: error: Could not resolve package dependencies:
  Failed to clone repository https://github.com/[ユーザー名]/[存在しないリポジトリ]:
    ...
    fatal: repository 'https://github.com/[ユーザー名]/[存在しないリポジトリ]/' not found
$ echo $?
74

解決とビルドを別のステップにしておくと、ネットワークやリポジトリの問題によるエラーは、解決のステップで失敗する。ビルドのステップは、コンパイルエラーなどの失敗の確認に集中できる。

CIで使う

- name: Resolve package dependencies
  run: xcodebuild -resolvePackageDependencies -project [プロジェクト名].xcodeproj -scheme [スキーム名] -clonedSourcePackagesDirPath SourcePackages

- name: Build
  run: xcodebuild build -project [プロジェクト名].xcodeproj -scheme [スキーム名] -clonedSourcePackagesDirPath SourcePackages -disableAutomaticPackageResolution

参考: 【Xcode】Shared schemeをGit管理してCIのxcodebuildで使う (xcscheme)

Swiftパッケージの場合

Package.swiftを持つSwiftパッケージでは、swift package resolveで依存関係の解決だけを実行できる。Package.resolvedのバージョンを維持する動作は、xcodebuildと同じである。

$ swift package resolve
Working copy of [リポジトリのURL] resolved at [バージョン]

swift package resolveは、.xcodeprojだけがあるフォルダでは、Could not find Package.swiftのエラーになる。Xcodeプロジェクトの依存関係は、xcodebuild -resolvePackageDependenciesで解決する。

参考: 【swift package】Package.swiftの依存関係の解決だけを先に実行する (resolve)

どちらの方法を選ぶか

やりたいこと方法
依存関係の解決だけを実行する-resolvePackageDependencies
Package.resolvedのバージョンでビルドする-disableAutomaticPackageResolution
取得したパッケージを再利用する-clonedSourcePackagesDirPath

Package.resolvedをコミットしたプロジェクトでは、CIの最初のステップに-resolvePackageDependenciesを置き、ビルドには-disableAutomaticPackageResolutionを付ける。