Руководства по разработке

Как настроить проверку сборок iOS и Mac Catalyst

Как настроить проверку сборок iOS и Mac Catalyst

Когда в проекте, изначально рассчитанном только на 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 и пять узлов и выберите аренду на день, неделю, месяц или квартал в зависимости от рабочего цикла.

Выбрать облачный Mac