ITMS-91053: исправляем «Missing API declaration» в Expo
Автор: Mehdi Azizi
Опубликовано , 8 мин
ITMS-91053: билд использует API с обязательной причиной без декларации. Добавь категорию и код в ios.privacyManifests и пересобери билд.
ITMS-91053 это автоматическая проверка Apple, которая приходит письмом после отправки твоего билда. Она сообщает, что код твоего приложения использует API «с обязательной причиной» (настройки, даты файлов, время с момента загрузки, место на диске или клавиатуры) и не объясняет зачем в манифесте конфиденциальности. В Expo это исправляется в ios.privacyManifests: категория, которую назвала Apple, код, описывающий твоё реальное использование, и новый билд.
В Solys, моём будильнике с мини-играми, эти причины задекларированы в конфигурации приложения. Ниже его настоящая декларация, команда, которая показывает, что используют твои зависимости, и проверка итогового файла перед отправкой.
Что значит ошибка ITMS-91053?
Письмо называется «ITMS-91053: Missing API declaration». В нём сказано, что код конкретного файла твоего билда обращается к одному или нескольким API, которым нужна причина. Дальше идёт список категорий, например NSPrivacyAccessedAPICategoryUserDefaults.
Эти API не запрашивают у пользователя никакого разрешения. Но с их помощью можно опознать устройство, Apple называет это fingerprinting. Это запрещено, даже если пользователь согласился на отслеживание. Apple объясняет это на странице про API с обязательной причиной: каждое приложение и каждый SDK должны декларировать, зачем они их используют.
Там же закреплено правило: с 1 мая 2024 года App Store Connect не принимает приложение, которое не описывает это использование в своём манифесте. Некоторые разработчики получали это письмо как простое предупреждение, при принятом билде. Не рассчитывай на это: считай его блокером и исправь до следующей отправки.
Как и другие ошибки ITMS, это не отказ ревьюера. Никто не открывал твоё приложение: Apple проанализировала бинарник.
Privacy manifest в iOS: файл PrivacyInfo.xcprivacy
Манифест конфиденциальности это файл PrivacyInfo.xcprivacy в корне бандла приложения. Согласно документации Apple о privacy manifests, он описывает собираемые данные, отслеживание и API с обязательной причиной. Последние идут в массив NSPrivacyAccessedAPITypes: одна запись на категорию, с её типом и списком причин.
Главная ловушка: манифест покрывает только свой собственный бандл. Apple пишет прямо: каждый исполняемый файл или динамическая библиотека, использующие эти API, должны иметь манифест в своём бандле. Манифест твоего приложения не покрывает встроенный фреймворк, а SDK не может полагаться на манифест твоего приложения.
Не путай его со строками в Info.plist, которых требует ITMS-90683. Те показываются пользователю в запросе разрешения. Манифест же читает только Apple.
5 категорий API и их коды причин
Полное имя категории начинается с NSPrivacyAccessedAPICategory. Список API и причин есть на странице ключа NSPrivacyAccessedAPIType, прочитанной 15 октября 2026 года.
| Категория | Типичный API | Частая причина |
|---|---|---|
| UserDefaults | UserDefaults | CA92.1 |
| FileTimestamp | creationDate | C617.1 |
| SystemBootTime | systemUptime | 35F9.1 |
| DiskSpace | volumeAvailableCapacityKey | E174.1 |
| ActiveKeyboards | activeInputModes | 54BD.1 |
Коды, которые ты будешь встречать чаще всего:
- CA92.1: приложение читает и пишет свои собственные настройки, недоступные другим приложениям.
- 1C8F.1: настройки, общие с виджетом или расширением из той же App Group.
- C617.1: даты или размер файлов, лежащих в контейнере приложения.
- 3B52.1: файлы, выбранные пользователем, например в окне выбора документов.
- 35F9.1: измерить время между двумя событиями в приложении или запустить таймер.
- E174.1: проверить свободное место перед записью файлов, с видимым для пользователя эффектом.
- 85F4.1: показать пользователю место на диске.
Декларируй причину, которая соответствует твоему реальному использованию, а не ту, что выглядит самой широкой. Apple уточняет, что эти API и их данные служат только задекларированным причинам и никогда не используются для отслеживания. Два кода зарезервированы для сторонних SDK: C56D.1 и 0A2A.1. В манифесте твоего приложения им не место.
Декларируем privacy manifest в Expo
В Expo ты не пишешь файл сам. Ты описываешь его в expo.ios.privacyManifests, в app.json или app.config, а prebuild генерирует PrivacyInfo.xcprivacy. Это способ из документации Expo о privacy manifests.
Вот реальный фрагмент конфигурации Solys. В ключе есть ещё отслеживание и собираемые данные, здесь я их убрал:
{
"expo": {
"ios": {
"privacyManifests": {
"NSPrivacyAccessedAPITypes": [
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
"NSPrivacyAccessedAPITypeReasons": ["CA92.1", "1C8F.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryFileTimestamp",
"NSPrivacyAccessedAPITypeReasons": ["C617.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategorySystemBootTime",
"NSPrivacyAccessedAPITypeReasons": ["35F9.1"]
}
]
}
}
}
}
CA92.1, C617.1 и 35F9.1 повторяют то, что декларируют React Native и модули Expo в приложении. 1C8F.1 идёт от самого Solys: приложение делится настройками со своим виджетом через App Group.
Ловушка, о которой стоит знать: если папка ios генерируется и игнорируется git, PrivacyInfo.xcprivacy, написанный руками в этой папке, никогда не попадёт в билд. EAS заново запускает prebuild и перегенерирует папку. Я убедился в этом на одном из своих проектов 9 сентября 2026 года: написанный вручную манифест пропал после prebuild. Декларация должна жить в конфигурации.
И последнее: манифест является частью бинарника. Обновление по воздуху его не меняет. Нужно пересобрать билд и отправить его заново.
Сторонние SDK со своим манифестом
Большинство свежих библиотек поставляют свой манифест. У пакетов Expo, использующих эти API, есть PrivacyInfo.xcprivacy в папке ios, как и у React Native. Проблема в том, как они линкуются с твоим приложением.
Документация Expo предупреждает: Apple не всегда правильно читает манифесты зависимостей CocoaPods, слинкованных статически, а пакеты Expo линкуются именно так. Поэтому их причины, возможно, придётся скопировать в конфигурацию твоего приложения.
Чтобы сократить эту работу, настройка ios.privacyManifestAggregationEnabled в expo-build-properties объединяет манифесты зависимостей CocoaPods в один файл. В Podfile, который Expo генерирует в моих проектах (SDK 57), это объединение включено, если ты его не отключишь.
У объединения есть предел: оно видит только зависимости, у которых есть манифест. Старая библиотека без манифеста остаётся невидимой. Поэтому читай путь к файлу, указанный в письме: он говорит, где исправлять.
| Указанный файл | Где исправлять |
|---|---|
| Исполняемый файл приложения | ios.privacyManifests |
.framework | Обновить SDK |
Расширение .appex | Манифест расширения |
Проверка перед отправкой
Expo предлагает дождаться письма от Apple после отправки. Это работает, но каждая итерация стоит билда. Вот четыре проверки, которые я делаю заранее.
1. Составь список того, что декларируют твои зависимости. Из корня проекта:
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
-exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
На Solys 15 октября 2026 года команда находит 13 манифестов и четыре категории: даты файлов, настройки, время с момента загрузки и место на диске. Каждая найденная категория должна оказаться в итоговом манифесте приложения или в манифесте фреймворка, который её использует.
2. Поищи в своём нативном коде. Свои модули, плагины, таргеты вроде виджета: этот код ни один SDK за тебя не задекларирует.
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
--include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
Подставь папки своего проекта. На Solys именно этот поиск выявляет настройки, общие через App Group, отсюда код 1C8F.1.
3. Прочитай сгенерированный манифест. Запусти npx expo prebuild -p ios, затем открой ios/<YourAppName>/PrivacyInfo.xcprivacy. Внимание: эта команда может удалить и пересоздать папку ios. Если ты вносил туда правки руками, работай с копией проекта.
4. Проверь итоговый билд. Это единственное доказательство, которое имеет значение. Скачай IPA со страницы билда на expo.dev и запусти на своём Mac:
unzip -q MyApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
Вторая строка выводит все встроенные манифесты: приложения и фреймворков. Третья показывает манифест приложения, его нужно сравнить со списком из шага 1.
Манифест должен совпадать с твоей карточкой
Манифест не существует сам по себе. Если он декларирует отслеживание или собираемые данные, твоя карточка в App Store и политика конфиденциальности должны говорить то же самое. Расхождение между ними тремя может потом обернуться отказом от живого ревьюера по guideline 5.1.1. Что именно нужно согласовать, подробно описано в статье политика конфиденциальности приложения.
Отправь билд без неприятных сюрпризов
Ошибки ITMS исправляются быстро, если умеешь их читать. Отказы, которые приходят потом, требуют больше вдумчивости: порядок действий есть на странице приложение отклонено Apple, а самые частые причины в статье почему Apple отклоняет приложение.
Чтобы проверить всё перед отправкой, в Apple Review Kit собраны мой чек-лист и skills для Claude Code, которые проверяют твой проект. Если отправка застряла и тебе нужен взгляд живого человека, забронируй час со мной: разберём твой проект по видеосвязи, на твоём экране. А если готовишь свой первый релиз, пройди бесплатную диагностику: она расставит шаги по порядку и пришлёт тебе мой чек-лист против отказов Apple.
Частые вопросы
Блокирует ли ITMS-91053 отправку билда?
Apple пишет, что с 1 мая 2024 года App Store Connect не принимает приложения, которые не описывают использование этих API. При этом некоторые разработчики получали письмо при принятом билде, как предупреждение. В обоих случаях исправь манифест до следующего билда.
Какой код причины выбрать для UserDefaults?
CA92.1, если приложение только читает и пишет свои собственные настройки. Добавь 1C8F.1, если оно делится ими с виджетом или расширением через App Group. C56D.1 зарезервирован для сторонних SDK: не добавляй его в манифест своего приложения.
Нужно ли копировать манифест каждого SDK в app.json?
Не обязательно. Если объединение манифестов включено, манифесты зависимостей CocoaPods собираются вместе при установке pods. Копируй категорию, только если её нет в итоговом манифесте, с причиной, описывающей реальное использование.
Нужен ли моему виджету или расширению свой манифест?
По словам Apple, каждый бандл с исполняемым файлом, который использует эти API, должен иметь свой манифест. Расширение это отдельный исполняемый файл в собственном бандле: манифест приложения его не покрывает.
Можно ли исправить ITMS-91053 без нового билда?
Нет. Манифест конфиденциальности является частью бинарника. Исправь конфигурацию, пересобери билд и протестируй его в TestFlight, прежде чем отправлять на ревью.