Site icon AppTractor

Прощай, project.pbxproj, здравствуй, JSON: крупнейшее изменение Xcode за 30 лет

Apple наконец заменила самый пугающий файл в каждом iOS-репозитории. Вот что изменилось, как перейти на новый формат и стоит ли это делать — с объяснениями для джунов, сеньоров и тимлидов.

Проблема, которую знает каждый iOS-разработчик

Если вы когда-нибудь открывали pull request, добавляющий всего два Swift-файла, и видели, как project.pbxproj внезапно разрастается на 30+ строк изменений с такими строками:

7B2A1F4E2C8D9A3B00F1E2D3 /* PaymentView.swift in Sources */ = {
    isa = PBXBuildFile;
    fileRef = 7B2A1F4D2C8D9A3B00F1E2D3 /* PaymentView.swift */;
};

…то вы уже понимаете, почему это изменение важно. Десятилетиями стандартная командная политика при проверке diff-файлов pbxproj выглядела примерно так: «одобрить с закрытыми глазами и надеяться, что сегодня больше никто его не трогал».

С Xcode 27.2 эта эпоха заканчивается. Файл конфигурации проекта теперь использует JSON.

Предыстория: что такое project.pbxproj и почему он причинял столько боли

Внутри каждого пакета .xcodeproj находится один конфигурационный файл, описывающий весь проект: какие файлы существуют, к каким target они относятся, настройки сборки и фазы сборки.

До сих пор этим файлом был project.pbxproj — ASCII-формат property list, доставшийся Apple ещё от NeXTSTEP, то есть ему уже больше 30 лет. У него было три фундаментальные проблемы:

1. Плоский граф объектов, склеенный 24-символьными шестнадцатеричными ID. Каждый файл, группа, target и build phase представляли собой объект с идентификатором вроде 7B2A1F4E2C8D9A3B00F1E2D3, а сами объекты ссылались друг на друга по этим ID из совершенно разных частей файла. Человек это нормально читать не мог, а инструментам приходилось заниматься реверс-инжинирингом формата.

2. Одно действие означало изменения в 3–4 местах. Добавление одного файла заставляло Xcode записывать PBXFileReference, PBXBuildFile, запись в PBXGroup и запись в build phase Sources нужного таргета — четыре разрозненных изменения ради одного клика.

3. Это была фабрика merge-конфликтов. Когда два разработчика добавляли файлы в разных ветках, git видел два набора неупорядоченных изменений, изобилующих ID и попадающих в пересекающиеся области одного огромного файла. В результате появлялся страшный <<<<<<< HEAD внутри файла, который человек не мог безопасно править вручную. Во многих командах неофициально существовал отдельный «специалист по исправлению конфликтов pbxproj».

Что нового в Xcode 27.2

В Xcode 27.2 и новее конфигурацией проекта по умолчанию становится project.xcproj — более компактный, иерархический и самоописываемый файл в формате JSON. Из официальной документации Apple:

«В Xcode 27.2 и новее файлом конфигурации проекта по умолчанию является более компактный, иерархический и самоописываемый JSON-файл с расширением .xcproj. Xcode 27 и новее поддерживает оба формата файлов, поэтому вы можете выбрать тот, который предпочитаете».

Вот как выглядит минимальный проект в новом формате:

{
  "default-configuration": "Debug",
  "configurations": ["Debug", "Release"],
  "files": [
    {
      "kind": "group",
      "path": "Sources",
      "children": [
        { "path": "PaymentView.swift", "target-membership": ["MyApp/compile-sources"] }
      ]
    }
  ],
  "targets": [
    {
      "name": "MyApp",
      "build-settings": {
        "IPHONEOS_DEPLOYMENT_TARGET": "27.0",
        "SWIFT_OPTIMIZATION_LEVEL[config=Debug]": "-Onone"
      }
    }
  ]
}

Его сможет прочитать даже джун. Ревьюер сможет нормально проверить изменения. Git сможет их нормально слить.

Что решает новый формат

Обратные связи. Вместо того чтобы target перечисляли файлы через цепочки ID-ссылок, теперь каждый файл сам объявляет своё членство в target через target-membership. Добавление файла теперь означает одну строку в одном месте, а не четыре изменения в четырёх секциях. Уже одно это устраняет большую часть исторических merge-конфликтов.

ID почти исчезают. Идентификаторы остаются только там, где они действительно нужны, например у target и продуктов. Diff больше не выглядит как утечка ключей шифрования.

Настройки сборки схлопываются. Вместо дублирования целых блоков XCBuildConfiguration для каждой конфигурации условие можно указать прямо в одной строке настройки: "SETTING[config=Debug]": "value". В одном из примеров сообщества примерно 300 строк property list превратились примерно в 123 строки JSON.

Diff теперь соответствует вашим действиям. В документации Apple говорится, что изменения в режиме сравнения теперь соответствуют тому, что вы реально сделали в Xcode. Переименовали файл? Diff покажет переименование файла, а не пятнадцать изменившихся шестнадцатеричных ссылок.

Меньше merge-конфликтов. Поскольку структура JSON иерархическая и изолирует изменения конфигурации, два разработчика, добавляющие файлы в разные группы, просто не сталкиваются друг с другом.

Coding agents могут безопасно редактировать проекты. Apple явно проектировала формат с расчётом на эпоху ИИ — в документации он называется «более удобным для редактирования агентами coding intelligence». Агент, добавляющий файл, теперь вставляет один JSON-узел вместо синхронизации четырёх перекрёстно связанных таблиц. Apple также выпустила open-source Swift-библиотеку xcode-project-format, которая даёт инструментам типизированный доступ к структуре проекта, поэтому экосистеме больше не нужно заниматься реверсом формата.

Как перейти

Новые проекты — ничего делать не нужно

Любой проект, созданный в Xcode 27.2 или новее, использует формат JSON по умолчанию. Вы получаете project.xcproj с самого начала, без каких-либо флагов или дополнительных настроек.

Существующие проекты — один выпадающий список

Существующие проекты не конвертируются автоматически; Apple оставляет решение за вами. Переход занимает считаные секунды:

  1. В Project navigator выберите проект.
  2. Откройте File inspector (⌥⌘1).
  3. В разделе Project Document откройте выпадающее меню Project Format и выберите JSON.
  4. Если появится диалог, нажмите Continue.

Внутри вашего .xcodeproj файл project.pbxproj будет удалён, а project.xcproj — добавлен. Закоммитьте оба изменения вместе отдельным коммитом с понятным сообщением.

Для автоматизации во множестве репозиториев xcodebuild предлагает вызов -convert-project, а Apple также поставляет инструмент xcprojformatter для проверки и форматирования .xcproj-файлов.

Откат

Если вы используете систему контроля версий, отменить переход очень просто, и это официально задокументировано: отмените два изменения файлов — удаление .pbxproj и добавление .xcproj — и проект вернётся к старому формату.

Чек-лист перед миграцией

Плюсы и минусы

Плюсы

Минусы

Кому и когда стоит переходить

Переходите сейчас, если вся команда уже на Xcode 27+, проект управляется напрямую в Xcode, а конфликты pbxproj регулярно отнимали время. Улучшенные diff и снижение числа конфликтов окупают десятисекундную конвертацию практически сразу.

Переходите позже, если вы сильно зависите от CocoaPods или других инструментов, изменяющих проект, либо если кто-то из команды или какая-то CI-машина всё ещё работает на старом Xcode. Для новых проектов уже используйте JSON, а существующие переводите после того, как ваши инструменты добавят поддержку.

Ничего срочного не меняется, если источником истины у вас является XcodeGen или Tuist — сгенерированный проект у вас всё равно одноразовый. Переходите на новый формат, когда ваш генератор начнёт создавать .xcproj.

Итог

Apple заменила 30-летний, забитый ID и постоянно конфликтующий plist на чистый иерархический JSON-формат, который могут читать люди, нормально сливать git и редактировать ИИ-агенты. Миграция выполняется одним выбором в меню, полностью обратима и ограничивается только версией Xcode в вашей команде и совместимостью инструментов. Для большинства команд это редкий пример платформенного изменения, которое почти ничего не стоит, но окупается в каждом спринте.

Exit mobile version