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
Маніфест не існує сам по собі. Якщо він декларує відстеження чи зібрані дані, твоя сторінка в 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, перш ніж відправляти на рев'ю.