Connect with us

Интегрированные среды разработки

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

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

Опубликовано

/

     
     

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+? Формат JSON совместим только с Xcode 27 и новее. Коллега на Xcode 26 не сможет открыть конвертированный проект — это жёсткое ограничение.
  • Проверьте CI и инструменты. Всё, что парсит или изменяет файл проекта, должно понимать JSON: CocoaPods, XcodeGen, Tuist/XcodeProj, Capacitor, плагины fastlane, собственные скрипты. Поддержка быстро появляется по всей экосистеме, но перед конвертацией проверьте каждый инструмент отдельно.
  • Конвертируйте в спокойный момент. Делайте это отдельным PR без других изменений, желательно сразу после релиза, чтобы долгоживущим веткам не пришлось ребейзиться через границу смены формата.
  • Понимайте масштаб. Меняется только файл конфигурации проекта. Schemes, Package.resolved, данные workspace и пользовательские данные не затрагиваются. Сама сборка не меняется — меняется только её описание.

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

Плюсы

  • Радикально меньше merge-конфликтов в самом конфликтном файле большинства iOS-репозиториев — это реально возвращает команде часы времени в каждом спринте.
  • Diff можно нормально ревьюить. Изменения файла проекта снова становятся полноценной частью code review, а не местом, где все просто нажимают Approve.
  • Меньший и читаемый человеком файл, чья иерархия отражает то, что вы видите в Project navigator.
  • Полноценная поддержка ИИ-инструментов. Coding agents, включая собственные интеллектуальные функции Xcode, могут надёжно редактировать проект, а open-source библиотека xcode-project-format даёт сторонним инструментам официальную основу.
  • Низкорисковая и обратимая миграция с официально задокументированным Apple путём отката и поддержкой обоих форматов бок о бок в Xcode 27.

Минусы

  • Жёсткое требование Xcode 27+. Один человек в команде или один CI-образ на старой версии Xcode блокирует переход для всех.
  • Экосистема пока отстаёт. Инструменты, которые читают или записывают pbxproj, ещё догоняют новый формат; некоторые, например Capacitor, сообщали о поломках уже в первую неделю после выхода 27.2. Если ваш pipeline сильно зависит от инструментов, изменяющих проект, лучше дождаться их обновлений.
  • Эволюция, а не революция. Это не вручную написанный manifest вроде Package.swift или DSL от Tuist. Xcode остаётся основным редактором; по-прежнему не стоит вручную писать этот файл как часть обычного рабочего процесса.
  • Документация пока скудная. Apple описывает миграцию, но не публикует полную спецификацию формата — Swift-библиотека сейчас ближе всего к роли справочника.

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

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

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

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

Итог

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

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: