Когда несколько разработчиков одновременно изменяют Storyboard или XIB, слияние XML может выглядеть успешным, хотя настоящие проблемы проявятся только при полной сборке: связанный объект уже удалён, файл интерфейса не компилируется, формат локализованных strings повреждён или одноимённые выходные файлы из двух модулей перезаписывают друг друга. Вместо того чтобы обнаруживать ошибку после запуска полной сборки на облачном Mac от ArmVMS, лучше заранее выполнить независимую проверку встроенным в Xcode инструментом ibtool.
Что именно может проверить ibtool
ibtool — это консольный компилятор ресурсов Interface Builder. Он может отдельно обрабатывать файлы .storyboard и .xib, выявляя ошибки в структуре XML, связях между объектами и некоторых настройках свойств, а также выводя предупреждения, возникающие при компиляции. Для такой проверки обычно не требуется предварительно компилировать код приложения, поэтому её удобно запускать перед коммитом или на раннем этапе CI.
Однако она не заменяет полную сборку. Существуют ли пользовательские классы в действительности, совместимы ли типы Swift, включены ли ресурсы в нужную цель и завершится ли финальная компоновка — всё это по-прежнему должен проверять xcodebuild. Оптимальная последовательность: сначала выполнить ibtool, а после успешной проверки запустить сборку и тесты целевого проекта.
Рассматривайте ibtool как средство синтаксической и структурной проверки ресурсов интерфейса, а не как инструмент окончательной приёмки. Его ценность — в быстром обнаружении ошибок, а не в охвате всей семантики сборки.
Перед началом убедитесь, что задача действительно использует нужный каталог разработчика. Это позволит избежать ситуации, когда в интерактивном сеансе и автоматизированной задаче выбраны разные версии Xcode:
xcode-select -p
xcodebuild -version
xcrun --find ibtool
Если на одном компьютере установлено несколько версий Xcode, не переключайте глобальную настройку в каждой задаче. Вместо этого явно задайте DEVELOPER_DIR для конкретного запуска и используйте одно и то же значение как для предварительной проверки, так и для последующей сборки.
Пакетная компиляция Storyboard и XIB
Следующий скрипт рекурсивно находит файлы интерфейса в проекте, пропуская каталоги зависимостей, результатов сборки и кешей. Имя выходного файла формируется с помощью хеша относительного пути, поэтому результаты не будут конфликтовать, даже если в разных модулях есть собственные файлы Main.storyboard.
#!/bin/bash
set -euo pipefail
export LANG=C
export LC_ALL=C
ROOT="${1:-$PWD}"
TMP="$(mktemp -d "${TMPDIR:-/tmp}/ibtool-check.XXXXXX")"
trap 'rm -rf "$TMP"' EXIT
status=0
while IFS= read -r -d '' file; do
rel="${file#"$ROOT"/}"
key="$(printf '%s' "$rel" | shasum -a 256 | cut -c1-12)"
log="$TMP/$key.log"
case "$file" in
*.storyboard) output="$TMP/$key.storyboardc" ;;
*.xib) output="$TMP/$key.nib" ;;
*) continue ;;
esac
printf 'Checking %s
' "$rel"
if ! xcrun ibtool \
--errors \
--warnings \
--notices \
--compile "$output" \
"$file" >"$log" 2>&1; then
cat "$log"
status=1
fi
done < <(
find "$ROOT" \
\( -path '*/Pods/*' \
-o -path '*/Carthage/*' \
-o -path '*/.build/*' \
-o -path '*/DerivedData/*' \) -prune \
-o \( -name '*.storyboard' -o -name '*.xib' \) \
-type f -print0
)
exit "$status"
Сочетание -print0 и read -d '' корректно обрабатывает пути с пробелами. Временный каталог удаляется через trap, поэтому скомпилированные ресурсы не остаются в рабочей копии. Журнал выводится только при ошибке в соответствующем файле, и логи CI не переполняются многочисленными сообщениями об успешных проверках.
Не компилируйте файлы непосредственно в каталог исходного кода
Файл .storyboardc фактически является каталогом, а .nib также может содержать несколько результатов компиляции. Если сохранять их рядом с исходниками, это может повлиять на состояние Git, сканирование ресурсов и последующие этапы упаковки. Все результаты предварительной проверки должны помещаться в отдельный временный каталог задачи и удаляться при её завершении.
Одновременная проверка ресурсов локализации
Проекты с Base Localization обычно содержат один базовый Storyboard и отдельные файлы .strings для каждого языка. Файл интерфейса может успешно компилироваться, даже если один из strings-файлов повреждён из-за кавычек, escape-последовательностей или маркеров конфликта. Сначала проверьте формат с помощью plutil:
find "$PWD" -type f \
\( -name '*.strings' -o -name '*.stringsdict' \) \
-not -path '*/Pods/*' \
-print0 |
while IFS= read -r -d '' file; do
plutil -lint "$file"
done
Если нужно проверить локализуемые ключи базового интерфейса, можно создать временный список:
xcrun ibtool \
--generate-strings-file /tmp/Main.generated.strings \
App/Base.lproj/Main.storyboard
plutil -lint /tmp/Main.generated.strings
Не заменяйте поддерживаемые вручную переводы сгенерированным файлом. Порядок записей и комментарии могут меняться между версиями Xcode. Надёжнее извлечь набор ключей и проверить, включены ли новые ключи в процесс перевода и не остались ли в нём устаревшие.
Превращение диагностики в поддерживаемый CI-контроль
Ошибка предварительной проверки должна немедленно останавливать текущий этап, но предупреждения следует классифицировать. Стоит регистрировать уведомления о совместимости макета, отсутствующих метках доступности и устаревших свойствах. При этом новые диагностические сообщения, появившиеся в очередной версии Xcode, не должны внезапно блокировать все ветки без предварительной оценки.
| Проявление | Распространённая причина | Способ устранения |
|---|---|---|
| Ошибка разбора XML | Маркеры конфликта слияния или обрезанный файл | Вернуться к коммиту с конфликтом и исправить его, не удаляя вручную неизвестные узлы |
| Выходной файл перезаписывается | В нескольких модулях есть одноимённые файлы интерфейса | Формировать имя выходного файла с помощью хеша относительного пути |
| Файл локализации не разбирается | Ошибки в кавычках, точках с запятой или escape-последовательностях | Выполнить plutil -lint для всех strings- и stringsdict-файлов |
| Локальная проверка проходит, а CI завершается ошибкой | Различаются Xcode или DEVELOPER_DIR |
Записывать версию в журнал и унифицировать окружение задачи |
| Количество предупреждений постоянно меняется | Изменилась версия инструмента или нестабилен охват сканирования | Зафиксировать исключаемые каталоги и проверять разницу для новых предупреждений |
Для небольших изменений можно проверять только файлы интерфейса, изменённые в текущей ветке. В основной ветке или периодической задаче следует сканировать весь репозиторий. Инкрементальная проверка обеспечивает быструю обратную связь, а полная помогает выявлять упущения после переименований, удалений и межмодульных изменений. Эти подходы не должны заменять друг друга.
Окончательная проверка с помощью полной сборки
После успешного выполнения ibtool запустите хотя бы одну сборку для симулятора, не требующую подписи. Она подтвердит, что ресурсы интерфейса корректно работают вместе с кодом, членством в целях и другими ресурсами:
: "${WORKSPACE:?Set WORKSPACE to the workspace path}"
: "${SCHEME:?Set SCHEME to the shared scheme}"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO \
build
Итоговый контроль должен гарантировать, что предварительная проверка и полная сборка используют одну версию Xcode, исключения в скрипте соответствуют каталогам зависимостей проекта, временные результаты не попадают в репозиторий, а журналы ошибок сохраняют исходные относительные пути. Тогда проблемы ресурсов интерфейса будут выявляться за несколько секунд, а ошибки, для диагностики которых нужен контекст всего проекта, перейдут на этап полной сборки. Это также сделает границы поиска неисправностей более ясными.
Часто задаваемые вопросы
Заменяет ли проверка ibtool полную сборку Xcode?
Нет. Она проверяет отдельные файлы интерфейса, но типы исходного кода, настройки сборки, связывание и итоговый пакет необходимо проверять через xcodebuild.
Почему нельзя формировать имя результата только из имени исходного файла?
В разных модулях могут находиться одноимённые Main.storyboard или View.xib. Хеш относительного пути либо сохранение структуры каталогов исключает перезапись результатов.
Нужно ли останавливать CI при любом предупреждении ibtool?
Надёжнее зафиксировать проверенный набор текущих предупреждений и блокировать только новые. После обновления Xcode формулировки диагностик могут измениться.
Выберите выделенный физический узел Apple Silicon для следующей сборки
Арендуйте ArmVMS M4 и ArmVMS M4 Pro посуточно, понедельно, помесячно или поквартально. Конфигурация, узел и дополнительные опции отображаются для проверки перед оформлением заказа.