Главная / Блог / Xcode 27.2: ошибка компиляции Mac Catalyst? Руководство по исправлению 2026
ENGINEERING_BLOG · 2026.10.09

Xcode 27.2: ошибка компиляции Mac Catalyst? Руководство по исправлению 2026

Симптом: Xcode 27.2 сообщает, что символ не объявлен или не найден при компиляции Mac Catalyst.
Быстрое решение: сначала проверьте, относится ли символ к iOS 27.1 API и падает ли только цель Catalyst; если да, изолируйте этот участок условной компиляцией и повторите сборку обеих платформ.

Руководство для разработчиков, которые поддерживают общий код iOS и Mac Catalyst и должны отделить проблему доступности API от ошибки проекта.
Также для инженеров Xcode CI, которым нужно подтвердить результат на фактическом узле сборки, а не только на локальной машине.

Последняя проверка — 9 октября 2026 года; статус сверён с примечаниями Apple к Xcode 27.2 и документацией по условной компиляции. Это относится к описанному в примечаниях состоянию Beta 2, а не автоматически ко всем проектам, выпускам Xcode или более поздним версиям.

SECTION 01Диагностика ошибки Mac Catalyst в Xcode 27.2

Apple относит к известной проблеме Xcode 27.2 Beta 2 компиляцию Mac Catalyst-кода, использующего API, доступный только для iOS 27.1. Среди характерных сообщений — undeclared identifier, not found и cannot find. При совпадении симптомов проверяйте именно платформенную доступность API, прежде чем менять CI-узел или массово обновлять зависимости. Описание проблемы и обходного решения в примечаниях к выпуску не доказывает, что любой отказ Catalyst вызван этим дефектом.

Сравнение двух целей — первая развилка диагностики. Соберите один и тот же коммит для iOS и Mac Catalyst, сохранив одинаковые настройки проекта, схему и конфигурацию. Если обе сборки завершаются одинаково, вероятнее общая ошибка исходников, импорта или зависимости. Если iOS проходит, а Catalyst падает на конкретном символе, исследуйте его платформенную доступность и место использования.

Наблюдение Что проверять в первую очередь Следующий шаг
iOS и Catalyst падают на одной ошибке Общий исходный код, импорт модуля, версия или подключение зависимости Найдите первую первичную ошибку и воспроизведите сборку на чистом рабочем дереве
iOS проходит, Catalyst не находит отдельный символ Платформу, для которой объявлен API, и строку кода, где он вызывается Сверьте символ с документацией и проверьте условную компиляцию
Catalyst падает до компиляции исходника или сообщает о настройках сборки Активную схему, SDK, архитектурные и прочие Build Settings Сравните настройки двух целей и конфигурацию CI
Ошибка приходит из стороннего модуля Версию зависимости, её исходники и поддержку цели Catalyst Проверьте обновление, замену или временное исключение зависимости

У ошибки с похожим текстом могут быть и другие причины: зависимость не добавлена в нужную цель, модуль импортируется не в той конфигурации, локальная схема отличается от CI или дефект находится в собственном коде. Не делайте вывод по одной строке cannot find: зафиксируйте файл, строку, имя сборочной цели и первую ошибку компилятора, а затем сопоставьте их с результатом второй платформы.

Для сценариев, где iOS-приложение получает версию для Mac, полезно отдельно свериться с документацией Apple по созданию версии приложения для Mac Catalyst. Она помогает не смешивать назначение Catalyst с предположением, будто каждая функция и каждый API iOS автоматически доступны в macOS-цели.

Проверка Результат, который поддерживает гипотезу о границе API Что ослабляет гипотезу
Одна ревизия и одинаковая конфигурация для двух целей iOS собирается, Catalyst падает на конкретном символе Ошибки появляются в обеих целях на общем участке
Место возникновения Ошибка находится в коде, использующем API, связанный с iOS 27.1 Сбой возникает при разрешении пакета, импорте или настройке схемы
Повторный запуск с полным журналом Первичная ошибка повторяется на той же строке Catalyst-сборки Сообщение меняется между запусками или относится к другому этапу
Проверка зависимости Символ принадлежит коду приложения или доступному для правки модулю Ошибка исходит из зависимости, которую проект не контролирует

SECTION 02Платформенная граница и условная компиляция

Условная компиляция нужна, чтобы исключить участок исходного кода для неподходящей цели на этапе компиляции. Она отличается от обычного runtime-условия: проверка во время выполнения не спасает код, если компилятор не может разрешить имя API ещё до запуска приложения. Для Swift синтаксис платформенной проверки описан в руководстве Apple по директивам и условной компиляции. Для Objective-C ориентируйтесь на макрос TARGET_OS_MACCATALYST и проверяйте его доступность в контексте проекта.

Для кода приложения Swift граница может выглядеть так:

#if targetEnvironment(macCatalyst)
    // Реализация или исключение для Mac Catalyst
#else
    // Код для остальных поддерживаемых целей
#endif

Если API предназначен только для iOS, не помещайте вызов в ветку, которая компилируется для Catalyst. Но и обратный вариант — отключить весь функциональный блок для Mac без проверки — не считается полноценным исправлением. Решите, что должно происходить в Catalyst: альтернативная реализация, явное отсутствие функции или интерфейс, который не предлагает неподдерживаемую возможность.

В Objective-C применяйте TARGET_OS_MACCATALYST к самому минимальному фрагменту, который зависит от платформенного API. Не подменяйте проверку Catalyst слишком широкой проверкой macOS, если от этого меняется поведение других целей проекта. Если общий модуль используется несколькими приложениями, сначала установите, где именно задаются доступные платформы и сборочные флаги.

Документация Build Settings в Xcode пригодится, если результат локально и в CI расходится не из-за исходника, а из-за параметров сборки. Не исправляйте проблему изменением нескольких несвязанных настроек «на всякий случай»: после каждого изменения должно быть понятно, какая гипотеза проверяется и какой результат её подтверждает.

SECTION 03Порядок исправления и охват исходников

Работайте от минимального воспроизведения к изменению кода. Это позволяет не принять побочный эффект правки за подтверждение причины.

  1. Зафиксируйте исходное состояние. Сохраните хеш коммита, активную схему и конфигурацию, команду сборки, версию Xcode и выбранный SDK. Приложите полный лог, а не только последнюю строку ошибки. Время и параметры запуска важны, если локальная сборка и CI выполняются в разных окружениях.

  2. Разделите цели. Запустите компиляцию iOS и Mac Catalyst на одном коммите. Убедитесь, что в обоих запусках выбраны ожидаемые схемы, конфигурации и назначения. Если различаются настройки, сначала устраните эту разницу: сравнивать результаты, полученные на разных конфигурациях, ненадёжно.

  3. Найдите первичный символ и его владельца. Определите, находится ли строка в приложении, общем модуле или сторонней зависимости. Для символа проверьте платформу доступности в документации и выясните, действительно ли он относится к iOS 27.1. Ошибка на уровне импорта или разрешения зависимости требует другого исправления, даже если текст компилятора похож.

  4. Ограничьте код на уровне компиляции. Добавьте условие Swift или Objective-C вокруг минимального участка, зависящего от API. Не используйте runtime-проверку как замену: компилятор всё равно должен обработать недоступное имя. Для общей функции выберите поведение Catalyst явно — альтернативу, отсутствие функции или иной пользовательский путь.

  5. Проверьте общие модули и зависимости. Изменение в общем исходнике способно случайно убрать функцию и из iOS-цели. Если проблема находится в недоступном для редактирования пакете, запишите его версию и цель, через которую он подключён. Затем сравните варианты: обновить пакет, заменить его либо временно не включать соответствующий путь в сборку Catalyst.

  6. Повторите сборки обеих платформ. Используйте ту же ревизию после исправления и отдельно запускайте iOS и Catalyst. Убедитесь, что iOS-функция не пропала, а Catalyst больше не обращается к проблемному символу. Если сборочный процесс включает тестирование или архивирование, проверьте эти этапы отдельно: успешная компиляция не подтверждает автоматически корректность тестов или релизного артефакта.

  7. Повторите проверку в Xcode CI. Запустите те же цели на фактическом узле, который отвечает за Apple-сборки. Сохраните версии инструментов, SDK, параметры запуска и полный журнал для каждой цели. Для этапа распространения используйте соответствующую процедуру: Apple отдельно описывает подготовку приложения к тестированию и выпуску и распространение на зарегистрированные устройства.

Для быстрой сортировки результатов применяйте условия, а не догадки:

  • Если один коммит проходит iOS, а Catalyst падает на символе, который официально ограничен iOS, то изолируйте минимальный участок условной компиляцией и проверьте альтернативное поведение Catalyst.
  • Если обе цели падают на одной общей ошибке, то вернитесь к исходникам, импортам и зависимостям: сначала проверьте код, а не удалённый узел.
  • Если локально обе цели проходят, но CI падает, то сравните коммит, Xcode, SDK, схему, параметры сборки и состояние зависимостей на узлах.
  • Если сбой исходит из зависимости, которую вы не можете изменить, то зафиксируйте версию и цель, после чего оцените обновление, замену или временное исключение.
  • Если более поздний Xcode заявляет об исправлении, то не удаляйте обход сразу: сопоставьте официальные примечания к нужной версии и результат сборки вашего проекта для обеих платформ.

SECTION 04Сценарий проверки и критерии приёмки

Представьте общий Swift-модуль: iOS-цель проходит, а сборка Catalyst останавливается на API, используемом в функции, которая вызывается из нескольких экранов. В такой ситуации рискованно обернуть условием весь модуль: это может скрыть не только один вызов, но и типы, необходимые обеим платформам. Сначала проследите путь от символа до его вызывающих участков, затем отделите платформенно-зависимую реализацию от общей логики.

Минимально достаточная проверка должна отвечать на три вопроса: ушла ли исходная ошибка Catalyst; сохранилась ли исходная функция в iOS; не появились ли новые ошибки при сборке, тестировании или формировании нужного артефакта. Если модуль общий, проверьте его потребителей, а не только таргет приложения. Если изменение касается зависимости, проведите тест и на конфигурации, в которой она используется, иначе успешная компиляция основного приложения может скрыть регрессию.

Результат приёмки должен быть воспроизводимым другим инженером. В записи укажите коммит, версию Xcode, SDK, цель, схему, внесённое изменение и ссылку на полный лог. Отдельно отметьте, какая ветка условной компиляции активна для iOS и Catalyst и какое поведение ожидается на каждой платформе. Не закрывайте задачу по одному локальному успешному запуску: он не показывает, что та же ревизия и настройки прошли на CI.

На момент проверки Apple описывает проблему именно в примечаниях к Xcode 27.2 Beta 2 и приводит условную компиляцию как обходной путь. Это ограниченный статус: он не подтверждает наличие того же дефекта во всех проектах, других версиях инструментов или в последующем официальном выпуске. При появлении новой Beta, RC или финальной версии снова прочитайте примечания к ней, затем проверьте собственный проект. Удаление временного кода оправдано только после двух подтверждений: официальная информация соответствует установленной версии, а нужные цели проекта проходят повторную сборку.

SECTION 05Решение по среде и закрытие инцидента

При оценке удалённой среды сначала отделите ошибку исходного кода от условий исполнения. Если одинаковый коммит, одна версия Xcode, один SDK и сопоставимые настройки дают разные результаты локально и в CI, тогда есть основания исследовать окружение: схемы, доступность зависимостей, параметры сборки и журналы запуска. Если же Catalyst падает на одном API и воспроизводится локально, перенос сборки на другой Mac не исправит платформенную границу.

У этого различения есть практическая цена. Перенос проекта на новый узел требует повторно проверить инструменты и параметры, а временное отключение проблемной функции может оставить неполноценную сборку. В свою очередь, условная компиляция без теста iOS способна скрыть регрессию общего кода. Поэтому инцидент закрывается не фактом «собралось», а сохранёнными результатами двух платформ и CI для одной ревизии.

Если вы регулярно принимаете такие сборки на отдельной macOS-машине, заранее определите, какие цели и артефакты будут проверяться на ней, а какие — в существующем CI. Для планирования можно посмотреть сведения о среде удалённого Mac и условия тарифов MACNOX; не переносите характеристики или доступность узлов из предположений в критерии приёмки — сверяйте их с актуальной информацией перед выбором.

Локальная машина удобна для интерактивной отладки, но не всегда повторяет CI; существующий CI может не давать вам контроля над узлом; а замена инфраструктуры добавляет отдельную работу по воспроизведению конфигурации и сбору журналов. Если проблема возникает эпизодически и вам нужна отдельная среда только для проверки Xcode CI, аренда удалённого Mac у MACNOX может быть разумнее покупки машины — при условии, что доступные условия подходят вашему графику и тестам. Для постоянно загруженного узла или требований к физическим интерфейсам сначала сравните аренду с собственным Mac и текущей инфраструктурой, а перед переносом инцидента убедитесь, что ошибка действительно зависит от среды.