Инструменты и релиз
Менеджеры зависимостей и XCFramework, подпись кода и provisioning, `.xcconfig` и секреты, символикация крашей, выбор линковки и безопасный выпуск.
9 вопросов
JuniorТеорияОчень частоЧто в подписи кода iOS доказывает каждый элемент — сертификат, provisioning profile, entitlement и App ID?
Что в подписи кода iOS доказывает каждый элемент — сертификат, provisioning profile, entitlement и App ID?
Сертификат доказывает, кто собрал, через ваш приватный ключ. App ID именует приложение по bundle id; entitlement'ы объявляют его возможности. Provisioning profile связывает сертификат, App ID, entitlement'ы и устройства, авторизуя сборку.
Типичные ошибки
- ✗Путают сертификат (кто подписал) с provisioning profile (что и где вправе запускаться)
- ✗Думают, что entitlement'ы даёт сертификат, а не объявляются и несутся в профиле
- ✗Считают, что для дистрибуции в App Store provisioning profile не нужен
Уточняющие вопросы
- →В чём разница между development и distribution provisioning profile?
- →Почему entitlement должен ещё и присутствовать в provisioning profile, чтобы работать?
JuniorТеорияЧастоЧто делают менеджеры зависимостей Swift Package Manager (SPM), CocoaPods и Carthage, и почему SPM стал стандартом по умолчанию?
Что делают менеджеры зависимостей Swift Package Manager (SPM), CocoaPods и Carthage, и почему SPM стал стандартом по умолчанию?
SPM — встроенный в Xcode менеджер от Apple на исходниках, собирающий пакеты из Git без лишних инструментов. CocoaPods — централизованный Ruby-инструмент, переписывающий workspace; Carthage лишь собирает фреймворки. SPM победил тем, что нативен.
Типичные ошибки
- ✗Думают, что SPM не умеет подключать бинарный фреймворк (XCFramework) — умеет через binary target
- ✗Считают, что CocoaPods и SPM не могут сосуществовать в одном проекте
- ✗Полагают, что Carthage переписывает проект Xcode так же, как CocoaPods
Уточняющие вопросы
- →Когда сегодня вы всё же выберете CocoaPods вместо SPM для новой зависимости?
- →Как SPM подключает предсобранный бинарный фреймворк как зависимость?
MiddleТеорияЧастоЧем отличаются build configuration, .xcconfig и scheme, и как выпустить Staging-сборку?
Чем отличаются build configuration, .xcconfig и scheme, и как выпустить Staging-сборку?
Build configuration — именованный набор build settings; .xcconfig поставляет их текстом вне файла проекта. Scheme выбирает, какую конфигурацию берёт действие. Staging получает свою конфигурацию и .xcconfig: bundle id, endpoint, подпись.
Типичные ошибки
- ✗Смешивают scheme (какое действие запускается) и build configuration (набор настроек)
- ✗Думают, что возможны только конфигурации Debug и Release
- ✗Зашивают переключение окружений в Info.plist вместо настроек на каждую конфигурацию
Уточняющие вопросы
- →Почему секреты нельзя держать в
.xcconfig, коммитящихся в Git? - →Как действие Archive у scheme решает, какую build configuration использовать?
MiddleДебаггингЧастоПодпись работает локально, но падает на CI с ошибкой об отсутствии профиля
Подпись работает локально, но падает на CI с ошибкой об отсутствии профиля
CI — чистая машина без login-keychain, поэтому нет ни сертификата, ни профиля, что накопил Mac, а автоподписи нужна сессия Apple ID, которой у CI нет. Чинится ручной подписью: установите их в keychain на раннере через fastlane match.
Открыть задачу →Типичные ошибки
- ✗Считают, что у CI тот же keychain и профили, что и на Mac разработчика
- ✗Полагаются на автоматическую подпись на headless-раннере без сессии Apple ID
- ✗Отключают подпись кода на CI вместо безопасной установки сертификатов
Уточняющие вопросы
- →Почему автоматической подписи нужна сессия Apple ID, которой нет у headless-раннера?
- →Как инструмент вроде fastlane match не оставляет сертификат в постоянном keychain раннера?
MiddleТеорияЧастоОтчёт о крэше из бета-сервиса TestFlight — сплошные hex-адреса. Что такое dSYM и как символицировать?
Отчёт о крэше из бета-сервиса TestFlight — сплошные hex-адреса. Что такое dSYM и как символицировать?
dSYM (отладочные символы) сопоставляет адреса с файлом, функцией, строкой. Release-сборки вырезают символы, поэтому без нужного dSYM отчёт — сырой hex. Символицируют с dSYM (по UUID) в Xcode Organizer или atos. Зависимости несут свои dSYM.
Типичные ошибки
- ✗Держат или загружают
dSYMне той сборки, поэтому UUID не совпадают - ✗Думают, что Release-сборки сохраняют символы так же, как Debug
- ✗Забывают, что сторонним предсобранным фреймворкам нужны свои
dSYM
Уточняющие вопросы
- →Как
dSYMсопоставляется с конкретной упавшей сборкой? - →Почему крэш в предсобранной зависимости остаётся несимволицированным даже с вашим
dSYM?
SeniorДизайнЧастоСпроектируйте CI/CD-пайплайн для iOS-приложения. Опишите, что запускается на каждый pull request, а что только когда изменение попадает в main; как секреты подписи — сертификаты, provisioning profile, API-ключи — держатся вне репозитория, но безопасно доходят до сборочной машины; как build configuration выбирают нужное окружение на каждом этапе; и как зелёная сборка main доходит до бета-сервиса TestFlight, а затем до App Store. Также укажите, какое время пайплайна вы считаете приемлемым для PR-пути против релизного, и как держите PR-путь быстрым, всё ещё гейтя каждый merge на тестах и проверках, не зависящих от подписи.
Спроектируйте CI/CD-пайплайн для iOS-приложения. Опишите, что запускается на каждый pull request, а что только когда изменение попадает в main; как секреты подписи — сертификаты, provisioning profile, API-ключи — держатся вне репозитория, но безопасно доходят до сборочной машины; как build configuration выбирают нужное окружение на каждом этапе; и как зелёная сборка main доходит до бета-сервиса TestFlight, а затем до App Store. Также укажите, какое время пайплайна вы считаете приемлемым для PR-пути против релизного, и как держите PR-путь быстрым, всё ещё гейтя каждый merge на тестах и проверках, не зависящих от подписи.
На каждый PR — быстрые проверки без подписи (сборка, тесты, линт), гейтящие merge. На main также подпишите и архивируйте: тяните сертификат/профиль из секрет-хранилища (fastlane match) во временный keychain, затем в TestFlight и App Store.
Типичные ошибки
- ✗Коммитят сертификаты подписи или API-ключи в репозиторий
- ✗Гоняют медленный подписанный архив на каждом PR, а не только на
main - ✗Зашивают секреты в
Info.plist, из-за чего они едут внутри бинарника
Уточняющие вопросы
- →Как секрет-хранилище отдаёт сертификат раннеру, не сохраняя его насовсем?
- →Какие проверки держать на PR-пути, чтобы он был быстрым, но всё же гейтил merge?
SeniorПроизводительностьИногдаЧистая сборка занимает 12 минут — что измерять и что реально её сокращает?
Чистая сборка занимает 12 минут — что измерять и что реально её сокращает?
Сначала измерьте тайминг сборки (-ftime-trace) — найдите медленные файлы и фазы. Аннотируйте дорогие выражения против вывода типов, разбейте на модули для изоляции, whole-module optimization — только для Release. Уберите лишние зависимости.
Типичные ошибки
- ✗Оптимизируют до измерения, какие файлы и фазы на самом деле медленные
- ✗Оставляют whole-module optimization для Debug, что убивает инкрементальные сборки
- ✗Игнорируют межмодульный вывод типов у неаннотированных сложных выражений
Уточняющие вопросы
- →Почему whole-module optimization вредит именно инкрементальным Debug-сборкам?
- →Как явные границы модулей ограничивают то, что пересобирает правка в одну строку?
SeniorДизайнИногдаПриложение тянет десяток сторонних пакетов через менеджер зависимостей SPM. Один выпускает ломающее изменение в минорном релизе, ваш следующий resolve молча его обновляет, и три модуля фич ломаются разом. Руководство хочет, чтобы обновления были осознанными и изолированными — обновление одной зависимости никогда не должно ломать все модули, что её касаются. Спроектируйте, как вы пиннуете, оборачиваете и вендорите зависимости: как ограничиваете версии в Package.swift и фиксируете их через Package.resolved, где проводите границы обёрток, чтобы сторонние типы не протекали между модулями, когда вендорите зависимость (форк или копия внутрь) вместо живой зависимости, и как держите весь граф проверяемым и воспроизводимым на CI и у каждого разработчика.
Приложение тянет десяток сторонних пакетов через менеджер зависимостей SPM. Один выпускает ломающее изменение в минорном релизе, ваш следующий resolve молча его обновляет, и три модуля фич ломаются разом. Руководство хочет, чтобы обновления были осознанными и изолированными — обновление одной зависимости никогда не должно ломать все модули, что её касаются. Спроектируйте, как вы пиннуете, оборачиваете и вендорите зависимости: как ограничиваете версии в Package.swift и фиксируете их через Package.resolved, где проводите границы обёрток, чтобы сторонние типы не протекали между модулями, когда вендорите зависимость (форк или копия внутрь) вместо живой зависимости, и как держите весь граф проверяемым и воспроизводимым на CI и у каждого разработчика.
Пиньте диапазоны в Package.swift, коммитьте Package.resolved — все машины и CI разрешают один граф. Оборачивайте библиотеку за свой протокол, чтобы типы не протекали — обновление затрагивает одну обёртку. Вендорите критичное, по одной.
Типичные ошибки
- ✗Используют открытые диапазоны версий и не коммитят
Package.resolved - ✗Дают сторонним типам протекать между модулями вместо обёртки за границей
- ✗Считают вендоринг бесплатным, игнорируя цену отслеживания фиксов upstream
Уточняющие вопросы
- →Чем коммит
Package.resolvedотличается от пиннинга диапазонов вPackage.swift? - →Когда обёртка-адаптер вокруг зависимости стоит дороже, чем экономит?
SeniorДизайнИногдаКоманда хочет выпускать новую версию каждую неделю, но должна уметь быстро остановить плохую сборку, пока она не дошла до большинства пользователей. Спроектируйте стратегию релиза целиком: как вы используете кольца в бета-сервисе Apple TestFlight (внутреннее, затем внешнее бета) для отлова регрессий до App Store, как применяете phased release в App Store, чтобы выкатывать версию на растущий процент пользователей, и как управляемые с сервера feature flag плюс kill switch позволяют выключить рискованную фичу без новой сборки и без ожидания ревью. Объясните, что вы отслеживаете во время выката, чтобы решить — продолжать, приостановить phased release или дёрнуть kill switch.
Команда хочет выпускать новую версию каждую неделю, но должна уметь быстро остановить плохую сборку, пока она не дошла до большинства пользователей. Спроектируйте стратегию релиза целиком: как вы используете кольца в бета-сервисе Apple TestFlight (внутреннее, затем внешнее бета) для отлова регрессий до App Store, как применяете phased release в App Store, чтобы выкатывать версию на растущий процент пользователей, и как управляемые с сервера feature flag плюс kill switch позволяют выключить рискованную фичу без новой сборки и без ожидания ревью. Объясните, что вы отслеживаете во время выката, чтобы решить — продолжать, приостановить phased release или дёрнуть kill switch.
Кольца: внутреннее TestFlight, внешнее бета, App Store. Phased release: выкатка ~7 дней, с паузой. Риск-фичи — за серверными флагами и kill switch, выключить любую удалённо, без сборки и ревью. Следите за crash-free rate, пауза при регрессии.
Типичные ошибки
- ✗Считают phased release необратимым, а не приостанавливаемым
- ✗Полагают, что kill switch требует отгрузки и ревью новой сборки
- ✗Пропускают кольца TestFlight, показывая регрессии сразу в App Store
Уточняющие вопросы
- →Почему серверный kill switch срабатывает быстрее ускоренного ревью App Store?
- →Какое падение crash-free rate оправдает приостановку phased release?