XcodeのプロジェクトファイルがJSON(.xcproj)に変わる|Xcode 27.2の新形式で変わるGit差分と、切り替え前に確認する3つのこと

AppleがXcode 27.2でプロジェクトファイルのJSON形式を導入した

Appleは、2026年9月16日に配布を始めたXcode 27.2ベータで、プロジェクトファイルの新しい形式を導入しました。Xcode 27.2のリリースノートには「JSONベースのプロジェクト形式(.xcproj)」として載っていて、読みやすく、マージしやすく、コーディングエージェント(コードを読み書きして作業を進めるAI)が編集しやすい形式だと説明されています。ベータ版は10月14日公開のベータ3まで更新が続いていて、Appleの開発者向け文書では、Xcode 27.2以降で作る新規プロジェクトはこの形式が既定になると書かれています。

Xcodeのプロジェクト(.xcodeproj)は実際にはフォルダで、その中の project.pbxproj というファイルに、ターゲット、ファイルの一覧、ビルド設定が記録されています。このファイルはAppleの古い設定ファイル形式(プロパティリスト)で書かれていて、1つの操作で離れた複数の場所が変わるため、Gitの差分が読みにくく、複数人で触るとマージ競合(2人が近い場所を変えてGitが自動で合体できない状態)が起きやすいことで知られてきました。新形式では、このファイルが project.xcproj というJSONファイルに置き換わります。

この記事では、Appleが公開した公式ライブラリで新形式のファイルを実際に生成し、旧形式と何が違うか、ファイルを1つ足したときの差分がどう変わるか、手元のXcode 26.5・27.0・27.1ベータで開けるかを確かめました。読み終えると、自分のプロジェクトを切り替えるか、チームやツールの対応を待つかを決められます。

3つの要点

Xcode 27.2から、project.pbxproj に代わる project.xcproj(JSON)が既定になる

何が変わる

各ファイルの行に、どのターゲットに入るかを書く

部品を一覧に並べてIDで結ぶ書き方から、Project navigatorと同じ階層へ。ファイルを1つ足したときの差分は1か所にまとまる。

開けるXcode

Xcode 27以降

手元ではXcode 27.0と27.1ベータでビルドが通り(27.1ベータでは単体テストも成功)、Xcode 26.5は開けなかった。

切り替え方

Xcode 27.2のFile inspectorで選ぶ

Project FormatをJSONに変えるだけ。Gitで変更を破棄すれば旧形式に戻せる。

中身はこう変わる:ファイルがターゲットを指す木構造

新形式の中身を確かめるために、Appleが公開した公式ライブラリ「xcode-project-format」を使いました。project.xcprojを読み書きするSwiftのライブラリで、2026年9月中旬にApache-2.0ライセンス(Runtime Library Exception付き)で公開され、Xcode自身がプロジェクトファイルを読み書きする処理にも使われているとライブラリの文書に書かれています。手元のSwift 6.4(Xcode 27.1ベータ付属)でビルドし、iOSアプリとその単体テストという2つのターゲットを持つ最小のプロジェクトの構造を、このライブラリのAPIを使ってSwiftコードで組み立て、project.xcprojとして書き出しました。

旧形式(project.pbxproj)と新形式(project.xcproj)の違い

旧形式:project.pbxproj

1つの平らな表に、すべての部品を横並びで記録

  • 構造:objects という1つの表に、ファイル参照、グループ、ターゲット、ビルドフェーズ、設定の一覧が並ぶ
  • 参照:24桁のIDで部品同士を結び付ける。差分にはIDの行が並ぶ
  • ファイルとターゲット:ターゲット側のビルドフェーズが「どのファイルを含むか」を持つ
  • 形式:Appleのプロパティリスト(古くからあるテキスト形式)

新形式:project.xcproj

Xcodeの画面と同じ階層で、ファイルがターゲットを指す

  • 構造:files(ファイルツリー)、targets、configurations、build-settings という木
  • 参照:名前とパスで結び付ける。IDは、ターゲットのようにファイルの外から参照される部品に付け、それ以外は名前で参照する
  • ファイルとターゲット:ファイル側が target-membership で「どのターゲットのどのフェーズに入るか」を持つ
  • 形式:JSON(末尾のカンマやコメントを許すJSON5)。既定値と同じ設定は書かれない

出典:Apple「xcode-project-format」の設計文書(file-format-goals、schema-overview)と、手元で生成したファイル(2026年10月19日)。

実際に書き出したファイルを、ビルド設定の一部と2つ目のターゲットを「...」で省略した骨格として載せます。ソースファイルの各行に target-membership があり、「DemoApp ターゲットの compile-sources(Compile Sources)フェーズに入る」ことを、ターゲット名とフェーズ名の組み合わせで表しています。ターゲット側には、ファイルの一覧がありません。

{
  "default-configuration": "Release",
  "configurations": [
    "Debug",
    "Release",
  ],
  "localizations": {
    "development": "en",
    "supported": [
      "Base",
    ],
  },
  "files": [
    {
      "kind": "group",
      "path": "DemoApp",
      "children": [
        { "path": "DemoApp.swift", "target-membership": [ "DemoApp/compile-sources" ] },
        { "path": "ContentView.swift", "target-membership": [ "DemoApp/compile-sources" ] },
        { "path": "Greeting.swift", "target-membership": [ "DemoApp/compile-sources" ] },
      ],
    }, {
      "kind": "group",
      "path": "DemoAppTests",
      "children": [
        { "path": "GreetingTests.swift", "target-membership": [ "DemoAppTests/compile-sources" ] },
      ],
    }, {
      "kind": "group",
      "name": "Products",
      "children": [
        { "path": "<PRODUCTS>/DemoApp.app", "type": "wrapper.application", "index": false },
        { "path": "<PRODUCTS>/DemoAppTests.xctest", "type": "wrapper.cfbundle", "index": false },
      ],
    },
  ],
  "targets": [
    {
      "name": "DemoApp",
      "id": "DEMOAPP0000000000000001",
      "product": "Products/DemoApp.app",
      "product-type": "application",
      "build-phases": [
        "compile-sources",
        "resources",
        "frameworks",
      ],
      "build-settings": {
        "GENERATE_INFOPLIST_FILE": "YES",
        "PRODUCT_BUNDLE_IDENTIFIER": "com.example.DemoApp",
        ...
      },
    }, ...
  ],
  "build-settings": {
    "IPHONEOS_DEPLOYMENT_TARGET": "17.0",
    "SDKROOT": "iphoneos",
    "SWIFT_OPTIMIZATION_LEVEL[config=Debug]": "-Onone",
    "SWIFT_OPTIMIZATION_LEVEL[config=Release]": "-O",
    ...
  },
}

上のファイルは、Appleの公式ライブラリで生成したproject.xcprojで、全体で112行・約3.6KBでした。同じファイル構成をXcodeGen(設定ファイルから.xcodeprojを生成するツール)で作った旧形式のproject.pbxprojは395行・約14KBです。旧形式は既定値と同じビルド設定も書き出す分、行数が多くなっています。

ファイルを読むときに知っておくと役立つ決まりが3つあります。1行に収まる小さな要素は1行で書かれ、ターゲットや設定のような大きな要素は複数行で書かれます。ビルド設定は「キー[config=Debug]」のように、構成ごとの値をキー名の中に書きます。最上位の項目として required-capabilities を置けて、将来の機能を使ったファイルを古いXcodeで開くと「新しいXcodeが必要」というエラーにする仕組みが用意されています。

ファイルを1つ足したときの差分は、4か所から1か所になった

Appleがこの形式で狙ったのは、「Xcodeで1つ操作したら、差分も1かたまりになる」ことです。設計文書には、ターゲットにファイルを1つ足したら差分のハンク(差分表示で1かたまりとして出る変更箇所)が1つになるべきだと書かれています。手元では、Xcodeの画面操作の代わりに、プロジェクトの定義にSettings.swiftを1つ加えて新旧それぞれのツールでファイルを作り直し、追加前後の差分をdiffコマンドで比べました。

Settings.swift を1ファイル追加したときの差分

旧形式 project.pbxproj

4か所・+4行

ファイル参照、ビルドファイル(ファイルをビルド対象として登録する項目)、グループの子要素、Compile Sourcesフェーズの4か所に、IDを含む行が1行ずつ増えた。

新形式 project.xcproj

1か所・+1行

グループの children に、ファイル名と target-membership を書いた1行が増えただけ。

旧形式はXcodeGen 2.45.3で、新形式は公式ライブラリで、それぞれ追加前後のファイルを作り直してdiffコマンドで比較(macOS、2026年10月19日)。

# 新形式 project.xcproj の差分(1ハンク)
@@ -18,6 +18,7 @@
         { "path": "DemoApp.swift", "target-membership": [ "DemoApp/compile-sources" ] },
         { "path": "ContentView.swift", "target-membership": [ "DemoApp/compile-sources" ] },
         { "path": "Greeting.swift", "target-membership": [ "DemoApp/compile-sources" ] },
+        { "path": "Settings.swift", "target-membership": [ "DemoApp/compile-sources" ] },
       ],

# 旧形式 project.pbxproj の差分(4ハンク、追加行だけ抜粋)
@@ -7,6 +7,7 @@
+  0A3B7A40E0D1C84AC8B907D1 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97BB02831BFEDDA2AD8FAF37 /* Settings.swift */; };
@@ -29,6 +30,7 @@
+  97BB02831BFEDDA2AD8FAF37 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
@@ -57,6 +59,7 @@
+        97BB02831BFEDDA2AD8FAF37 /* Settings.swift */,
@@ -146,6 +149,7 @@
+        0A3B7A40E0D1C84AC8B907D1 /* Settings.swift in Sources */,

旧形式で4か所に散るのは、ファイルの参照とターゲットへの所属が別々の部品として登録され、それぞれをIDで結ぶ構造だからです。2人が別々のファイルを足すと、同じ4か所の近くに行が増えるため、マージ競合が起きやすくなります。新形式では、ファイルの行にターゲットへの所属まで書くので、別々のファイルを足した2人の変更はファイルツリーの別の行に収まり、同じ4か所に変更が集まる旧形式より競合しにくくなります。

この比較は、ファイルをグループ(灰色のフォルダアイコン)で管理しているプロジェクトの話です。Xcode 16以降のフォルダ(青いフォルダアイコンで、ディスク上の中身を自動で取り込む方式)で管理しているプロジェクトでは、ファイルを足してもプロジェクトファイル自体は変わらないと設計文書にあり、どちらの形式でも差分は出ません。

開けるのはXcode 27以降:26.5は読めず、27.0と27.1ベータではビルドが通った

Appleの文書には、JSON形式はXcode 27以降と互換性があると書かれています。生成したproject.xcprojだけを置いたプロジェクトを、手元の3つのXcodeで開いて確かめました。手元にXcode 27.2ベータはないため、27.2での動作はAppleの文書を根拠にしています。

project.xcproj だけを持つプロジェクトを、各バージョンのxcodebuild(コマンドライン版のXcode)で開いた結果(macOS、Apple M4、2026年10月19日)。
Xcode(ビルド番号)開ける(-list でターゲット一覧を表示)ビルド・テストJSON形式への切り替え
26.5(17F42)開けない。「missing its project.pbxproj file」のエラー——
27.0(27A266a)開けるiOS Simulator向けビルド成功xcodebuild の -convert-project にJSONの選択肢がない
27.1 ベータ(27A9269)開けるビルド成功、単体テスト1件成功(iPhone 17 Proシミュレーター)同上
27.2 ベータ開ける(Appleの文書による)対象外(手元に27.2ベータなし)File inspector の Project Format で切り替え。新規プロジェクトは既定でJSON形式

Xcode 26.5では、.xcodeprojフォルダの中にproject.pbxprojがないという理由で開けませんでした。チームにXcode 26を使う人が1人でもいれば、その人はJSON形式のプロジェクトを開けません。Xcode 27.0と27.1ベータでは、エラーなく開けて、iOS Simulator向けのビルドが通り、27.1ベータでは単体テストも実行できました。Xcode 27.1は10月15日に正式版が公開されていますが、手元で使ったのはベータ版(27A9269)です。

一方で、Xcode 27.0と27.1ベータの xcodebuild には -convert-project というプロジェクト形式を変換するオプションがあるものの、選べる形式は「Xcode 2.4」から「Xcode 27.0」までの旧形式のバージョンだけで、JSON形式はありません。JSON形式への切り替えは、Xcode 27.2以降で行います。

JSON形式へ切り替える手順と、切り替える前に確認すること

切り替えの操作は、Appleの文書によると次の3手順です。

  1. Xcode 27.2で、Project navigatorでプロジェクトを選ぶ。
  2. File inspectorの「Project Document」にある「Project Format」のポップアップで「JSON」を選ぶ。
  3. 確認のダイアログが出たら「Continue」を押す。

この操作で、.xcodeprojの中のproject.pbxprojがproject.xcprojに置き換わります。戻したいときは、Gitで「project.pbxprojの削除」と「project.xcprojの追加」の変更を破棄します。

切り替える前に確認する3つのこと

確認1

チーム全員とCI(自動ビルドのサービス)がXcode 27以降か

Xcode 26以前はJSON形式のプロジェクトを開けない。手元のXcode 26.5は「project.pbxprojがない」というエラーになった。CIで使うmacOS環境のXcodeも確認する。

確認2

プロジェクトファイルを読み書きするツールが対応しているか

fastlane、CocoaPods、Tuist、XcodeGenのように project.pbxproj を直接読み書きするツールは、.xcproj への対応を個別に確認する。手元のXcodeGen 2.45.3が生成したのは project.pbxproj だった。Appleはツール作者向けに公式ライブラリを公開しているので、対応状況は各ツールの更新情報で確認する。

確認3

試験用のプロジェクトで差分を見てから本番へ

新規または複製したプロジェクトで切り替え、ファイルの追加やターゲット設定の変更をしてGitの差分を確認する。Xcode 27.2で新規作成するプロジェクトは既定でJSON形式になるので、確認1と2を満たさないチームでは、新規プロジェクトをチームへ共有する前に形式を確認する。

確認2のツール名は、InfoQ(2026年10月12日)が移行前の確認対象として挙げたfastlane・CocoaPods・Tuistと、手元で使ったXcodeGen。

切り替えで変わるのはファイルの書き方だけで、ターゲットやビルド設定の中身は同じです。Appleの設計文書でも、移行コストを最小にするために、プロジェクトの情報は変えずにファイルの表現だけを変えたと説明されています。チーム全員がXcode 27以降で、プロジェクトファイルを直接いじるツールを使っていないなら、Xcode 27.2が正式版になった時点で切り替えると、IDを追わずに、ファイル名とターゲット名でファイル追加やターゲット設定の差分を読めるようになります。まずは試験用のプロジェクトをJSON形式にして、ファイルを1つ足したときの差分を自分の目で確かめてみてください。

参考資料