Когда в проекте, изначально рассчитанном только на iPhone и iPad, включают Mac Catalyst, часто ошибочно полагают: «Если проект собирается для iOS, то и сборка для Mac должна пройти». На практике в автоматизированных заданиях на облачном Mac проблемы обычно связаны с заявленной поддержкой платформ в зависимостях, принадлежностью ресурсов к целям, условной компиляцией и совместным использованием кэшей. Надёжный подход — не просто добавить ещё один destination к существующей команде, а рассматривать обе платформы как отдельные продукты сборки с общей кодовой базой и независимой проверкой.
Сначала убедитесь, что проект действительно поддерживает обе цели
Сначала включите Mac Catalyst в настройках цели Xcode, а затем убедитесь, что соответствующие изменения файла проекта добавлены в репозиторий. Не полагайтесь только на состояние флажка в графическом интерфейсе: автоматизированное задание должно считывать итоговые значения из настроек сборки.
xcodebuild \
-project DemoApp.xcodeproj \
-scheme DemoApp \
-showBuildSettings |
grep -E 'SUPPORTS_MACCATALYST|PRODUCT_BUNDLE_IDENTIFIER|SDKROOT'
Значение SUPPORTS_MACCATALYST должно быть YES. Если проект использует рабочее пространство, замените -project на -workspace. Затем выполните xcodebuild -showdestinations и убедитесь, что общая схема предоставляет как destination для iOS Simulator, так и destination для Mac Catalyst. Если командная строка не находит схему, сначала проверьте, помечена ли она как Shared, вместо того чтобы многократно менять строку destination.
Кроме того, по отдельности проверьте диапазон поддерживаемых платформ для каждого Swift Package, бинарной зависимости и внутреннего модуля. Возможность собрать пакет для iOS ещё не означает, что в нём заявлена поддержка Mac Catalyst. Для закрытых бинарных зависимостей проверьте, содержит ли XCFramework соответствующий срез Catalyst. Если нужного среза нет, ошибка проявится только на этапе компоновки.
Разделяйте кэши и каталоги результатов
iOS и Catalyst создают разные кэши модулей, промежуточные объекты и результаты компоновки. Если в CI используется общий каталог DerivedData, сбой может зависеть от порядка выполнения заданий и временно исчезать после очистки. Рекомендуется назначить каждой платформе постоянный отдельный каталог и архивировать соответствующий пакет результатов.
set -euo pipefail
xcodebuild \
-workspace DemoApp.xcworkspace \
-scheme DemoApp \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath build/derived-ios \
-resultBundlePath build/results-ios.xcresult \
CODE_SIGNING_ALLOWED=NO \
build-for-testing
xcodebuild \
-workspace DemoApp.xcworkspace \
-scheme DemoApp \
-destination 'platform=macOS,variant=Mac Catalyst,arch=arm64' \
-derivedDataPath build/derived-catalyst \
-resultBundlePath build/results-catalyst.xcresult \
CODE_SIGNING_ALLOWED=NO \
build-for-testing
На этом этапе подпись отключена, чтобы проверка была сосредоточена на исходном коде, зависимостях и успешной компоновке. Архивирование и подпись для поставки следует выполнять в последующих заданиях, чтобы ошибки совместимости платформы и ошибки настройки подписи не смешивались в одном журнале.
Не используйте удаление всего каталога
~/Library/Developer/Xcode/DerivedDataкак стандартный способ исправления проблем. Точечное удаление каталога текущего задания не мешает другим процессам на том же компьютере и позволяет сохранить состояние сбойной сборки.
Сосредоточьте платформенные различия в одном слое
Жизненный цикл интерфейса, меню, поведение окон и некоторые системные возможности Catalyst отличаются от iOS. Условную компиляцию следует сосредоточить в адаптационном слое, а не распределять проверки targetEnvironment(macCatalyst) по бизнес-логике.
enum PlatformLayout {
static var usesDesktopNavigation: Bool {
#if targetEnvironment(macCatalyst)
return true
#else
return false
#endif
}
}
Платформенные API также требуют проверки доступности. Условная компиляция указывает только текущую цель сборки, но не гарантирует, что API доступен в установленной версии системы. Сначала определите стабильный интерфейс общего протокола, а затем создайте отдельные реализации для iOS и Catalyst. Это позволит модульным тестам напрямую проверять поведение на обеих платформах.
Ресурсы тоже необходимо проверять отдельно. Шрифты, файлы описаний конфиденциальности, локализованные ресурсы и модели данных могут попасть только в один продукт из-за пропущенного Target Membership. После сборки проверьте каталог продукта и убедитесь, что необходимые файлы присутствуют, вместо того чтобы обнаруживать пустой интерфейс только во время выполнения.
| Проверка | iOS | Mac Catalyst |
|---|---|---|
| Компоновка зависимостей | Доступен срез симулятора | Доступен срез Catalyst |
| Принадлежность ресурсов | Доступны в пакете App | Доступны в пакете Catalyst App |
| Условная компиляция | Ветка для мобильной платформы | Ветка адаптации для настольной платформы |
| Результаты проверки | Отдельный xcresult | Отдельный xcresult |
Разделите сбои на диагностируемые этапы
Задание для двух целей не должно завершаться единственным общим сообщением «сборка не удалась». Рекомендуется последовательно разделить процесс на разрешение зависимостей, сборку без подписи, модульные тесты, архивирование и приёмочную проверку поставки. Каждый следующий этап должен запускаться только после успешного завершения предыдущего, а журналы следует именовать по платформам.
При ошибке компиляции сначала проверьте четыре типа признаков
При ошибке No such module сначала проверьте заявленную поддержку платформ в зависимости и текущую цель сборки. При несовпадении архитектур проверьте бинарные срезы, а не добавляйте сразу глобальное исключение архитектуры. При ошибке unavailable API вернитесь в адаптационный слой и добавьте условную компиляцию вместе с проверкой доступности. При дублировании или отсутствии ресурсов проверьте Copy Bundle Resources и Target Membership.
Скрипты сборки также могут содержать предположения о платформе. Например, если SDK в скрипте жёстко задан как iphoneos, задание Catalyst будет обращаться к неверному пути. Скрипты должны использовать переменные PLATFORM_NAME, SDKROOT и TARGET_BUILD_DIR, предоставленные Xcode, и немедленно завершаться при обнаружении неизвестной платформы.
Составьте контрольный список перед слиянием
Минимальная проверка должна гарантировать, что для обеих целей из чистого каталога успешно выполняются разрешение зависимостей и сборка без подписи, а соответствующие файлы .xcresult сохраняются отдельно. Модульные тесты основных модулей должны охватывать интерфейсы платформенной адаптации. Для поведения, связанного с окнами, меню или перетаскиванием, следует дополнительно предусмотреть тесты только для Catalyst.
Перед отправкой изменений проверьте следующее:
- схема опубликована как Shared, а командная строка выводит оба destination;
- iOS и Catalyst используют раздельные каталоги DerivedData;
- зависимости явно поддерживают обе платформы, а набор бинарных срезов полон;
- проверки платформы сосредоточены в адаптационном слое, а общая логика не реализована повторно;
- ключевые ресурсы включены в оба продукта;
- журналы сбоев, пакеты результатов и снимки настроек сборки архивируются отдельно для каждой платформы;
- проверка компиляции выполняется отдельно от задания архивирования и подписи.
При первоначальной настройке в G-Mini или другой удалённой среде сборки запустите процесс два раза подряд, а затем поменяйте порядок выполнения двух целей. Если результат зависит от порядка, в первую очередь проверьте общий кэш, временные каталоги скриптов и неочищенные сгенерированные файлы. Признак стабильного процесса для двух целей — не случайное успешное завершение обеих сборок, а одинаковый результат из чистого рабочего пространства при любом порядке выполнения.
Часто задаваемые вопросы
Можно ли использовать один DerivedData для iOS и Mac Catalyst?
Для короткой локальной проверки можно, но в CI лучше разделять каталоги. Это исключает смешивание промежуточных файлов и упрощает очистку и диагностику.
Нужно ли сразу запускать полный набор тестов?
Нет. Сначала проверьте компиляцию без подписи и основные модульные тесты для обеих целей, затем добавьте UI-тесты, архивирование и проверку подписи.
Что проверять первым при сбое Catalyst?
Проверьте поддержку Catalyst в target, совместимость зависимостей, условную компиляцию, принадлежность ресурсов целям и платформенные build phases.
Облачный Mac для следующей очереди сборки
Сравните две конфигурации M4 и пять узлов и выберите аренду на день, неделю, месяц или квартал в зависимости от рабочего цикла.