ITMS-91053: jak naprawić Missing API declaration w Expo
Autor: Mehdi Azizi
Opublikowano , 8 min
ITMS-91053: twój build używa API wymagającego uzasadnienia, ale go nie deklaruje. Dodaj kategorię i kod w ios.privacyManifests, a potem zrób nowy build.
ITMS-91053 to automatyczna kontrola Apple, którą dostajesz mailem po wysłaniu builda. Oznacza, że kod twojej aplikacji używa API „wymagającego uzasadnienia” (preferencje, daty plików, czas od uruchomienia systemu, miejsce na dysku albo klawiatury), a jej manifest prywatności nie wyjaśnia, po co. W Expo naprawiasz to w ios.privacyManifests: kategoria wskazana przez Apple, kod opisujący twoje faktyczne użycie, a potem nowy build.
W Solys, moim budziku z minigrami, te uzasadnienia są zadeklarowane w konfiguracji aplikacji. Pokażę ci prawdziwą deklarację, polecenie, które wypisuje, czego używają twoje zależności, i kontrolę finalnego pliku przed wysyłką.
Co oznacza błąd ITMS-91053?
Wiadomość ma tytuł „ITMS-91053: Missing API declaration”. Mówi, że kod w konkretnym pliku twojego builda odwołuje się do jednego lub kilku API, które wymagają uzasadnienia. Potem wymienia kategorie, których to dotyczy, na przykład NSPrivacyAccessedAPICategoryUserDefaults.
Te API nie proszą użytkownika o żadną zgodę. Ale mogą służyć do rozpoznania urządzenia, co Apple nazywa fingerprintingiem. To jest zabronione, nawet gdy użytkownik zgodzi się na śledzenie. Apple wyjaśnia to na stronie o API wymagających uzasadnienia: każda aplikacja i każdy SDK musi zadeklarować, po co ich używa.
Ta sama strona ustala zasadę: od 1 maja 2024 App Store Connect nie przyjmuje aplikacji, która nie opisuje tych użyć w swoim manifeście. Niektórzy deweloperzy dostali ten mail jako zwykłe ostrzeżenie, przy przyjętym buildzie. Nie licz na to: traktuj go jak blokadę i popraw przed kolejną wysyłką.
Jak inne błędy ITMS, to nie jest odrzucenie przez reviewera. Nikt nie otworzył twojej aplikacji: Apple przeanalizowało binarkę.
Privacy manifest w iOS: plik PrivacyInfo.xcprivacy
Manifest prywatności to plik o nazwie PrivacyInfo.xcprivacy, umieszczony w katalogu głównym bundle'a aplikacji. Według dokumentacji Apple o privacy manifests opisuje zbierane dane, śledzenie i API wymagające uzasadnienia. Te ostatnie trafiają do tablicy NSPrivacyAccessedAPITypes: jeden wpis na kategorię, z jej typem i listą uzasadnień.
Pułapka: manifest obejmuje tylko własny bundle. Apple pisze to wprost: każdy plik wykonywalny lub biblioteka dynamiczna, która używa tych API, musi mieć manifest w swoim bundle'u. Manifest twojej aplikacji nie obejmuje osadzonego frameworka, a SDK nie może polegać na manifeście twojej aplikacji.
Nie myl go z tekstami w Info.plist, których wymaga ITMS-90683. Te wyświetlają się użytkownikowi w prośbie o zgodę. Manifest czyta tylko Apple.
5 kategorii API i ich kody uzasadnień
Pełna nazwa kategorii zaczyna się od NSPrivacyAccessedAPICategory. Listę API i uzasadnień znajdziesz na stronie klucza NSPrivacyAccessedAPIType, sprawdzonej 15 października 2026.
| Kategoria | Typowe API | Częste uzasadnienie |
|---|---|---|
| UserDefaults | UserDefaults | CA92.1 |
| FileTimestamp | creationDate | C617.1 |
| SystemBootTime | systemUptime | 35F9.1 |
| DiskSpace | volumeAvailableCapacityKey | E174.1 |
| ActiveKeyboards | activeInputModes | 54BD.1 |
Kody, na które trafisz najczęściej:
- CA92.1: aplikacja czyta i zapisuje własne preferencje, niedostępne dla innych aplikacji.
- 1C8F.1: preferencje współdzielone z widgetem lub rozszerzeniem z tej samej App Group.
- C617.1: daty lub rozmiar plików przechowywanych w kontenerze aplikacji.
- 3B52.1: pliki wybrane przez użytkownika, na przykład w selektorze dokumentów.
- 35F9.1: mierzenie czasu między dwoma zdarzeniami w aplikacji albo działanie timera.
- E174.1: sprawdzanie wolnego miejsca przed zapisem plików, z efektem widocznym dla użytkownika.
- 85F4.1: wyświetlanie użytkownikowi miejsca na dysku.
Deklaruj uzasadnienie, które odpowiada twojemu faktycznemu użyciu, a nie to, które wygląda na najszersze. Apple zaznacza, że te API i ich dane służą wyłącznie zadeklarowanym celom i nigdy śledzeniu. Dwa kody są zarezerwowane dla zewnętrznych SDK: C56D.1 i 0A2A.1. Nie mają miejsca w manifeście twojej aplikacji.
Deklarowanie privacy manifest w Expo
W Expo nie piszesz tego pliku sam. Opisujesz go pod expo.ios.privacyManifests, w app.json lub app.config, a prebuild generuje PrivacyInfo.xcprivacy. To metoda z dokumentacji Expo o privacy manifests.
Oto prawdziwy fragment konfiguracji Solys. Klucz zawiera też śledzenie i zbierane dane, które tu usunąłem:
{
"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 i 35F9.1 powtarzają to, co deklarują React Native i moduły Expo w aplikacji. 1C8F.1 pochodzi od samej Solys: aplikacja współdzieli ustawienia ze swoim widgetem przez App Group.
Pułapka, o której warto wiedzieć: jeśli twój folder ios jest generowany i ignorowany przez git, ręcznie napisany PrivacyInfo.xcprivacy w tym folderze nigdy nie trafi do builda. EAS ponownie uruchamia prebuild i generuje folder od nowa. Sprawdziłem to na jednym z moich projektów 9 września 2026: ręcznie napisany manifest zniknął po prebuildzie. Deklaracja musi żyć w konfiguracji.
Ostatnia rzecz: manifest jest częścią binarki. Aktualizacja zdalna go nie zmienia. Trzeba zrobić nowy build i wysłać go ponownie.
Zewnętrzne SDK z własnym manifestem
Większość nowszych bibliotek dostarcza własny manifest. Paczki Expo, które używają tych API, mają PrivacyInfo.xcprivacy w swoim folderze ios, podobnie jak React Native. Problem wynika ze sposobu, w jaki są linkowane z twoją aplikacją.
Dokumentacja Expo ostrzega: Apple nie odczytuje poprawnie wszystkich manifestów zależności CocoaPods linkowanych statycznie, a tak jest w przypadku paczek Expo. Może więc być konieczne przepisanie ich uzasadnień do konfiguracji twojej aplikacji.
Żeby ograniczyć tę pracę, ustawienie ios.privacyManifestAggregationEnabled z expo-build-properties scala manifesty zależności CocoaPods w jeden plik. W Podfile, który Expo generuje w moich projektach (SDK 57), to scalanie jest włączone, chyba że je wyłączysz.
Scalanie ma ograniczenie: widzi tylko zależności, które dostarczają manifest. Stara biblioteka bez manifestu pozostaje niewidoczna. Przeczytaj więc ścieżkę pliku podaną w mailu: mówi, gdzie poprawiać.
| Wskazany plik | Gdzie poprawić |
|---|---|
| Plik wykonywalny aplikacji | ios.privacyManifests |
.framework | Zaktualizuj SDK |
Rozszerzenie .appex | Manifest rozszerzenia |
Sprawdzenie przed wysyłką
Expo proponuje poczekać na maila od Apple po wysyłce. To działa, ale każda runda kosztuje build. Oto cztery kontrole, które robię wcześniej.
1. Wypisz, co deklarują twoje zależności. Z katalogu głównego projektu:
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
-exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
W Solys, 15 października 2026, polecenie znajduje 13 manifestów i cztery kategorie: daty plików, preferencje, czas od uruchomienia systemu i miejsce na dysku. Każda wypisana kategoria musi się znaleźć w finalnym manifeście aplikacji albo w manifeście frameworka, który jej używa.
2. Przeszukaj własny kod natywny. Własne moduły, pluginy, targety jak widget: tego kodu żaden SDK nie zadeklaruje za ciebie.
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
--include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
Dopasuj foldery do swojego projektu. W Solys to właśnie to wyszukiwanie pokazuje preferencje współdzielone przez App Group, stąd kod 1C8F.1.
3. Przeczytaj wygenerowany manifest. Uruchom npx expo prebuild -p ios, a potem otwórz ios/<YourAppName>/PrivacyInfo.xcprivacy. Uwaga: to polecenie może usunąć i odtworzyć folder ios. Jeśli wprowadziłeś w nim ręczne zmiany, pracuj na kopii projektu.
4. Sprawdź finalny build. To jedyny dowód, który się liczy. Pobierz IPA ze strony builda na expo.dev, a potem uruchom na swoim Macu:
unzip -q MyApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
Druga linia wypisuje wszystkie osadzone manifesty, aplikacji i frameworków. Trzecia pokazuje manifest aplikacji, który porównujesz z listą z kroku 1.
Co manifest musi mówić spójnie z twoją kartą
Manifest nie żyje sam. Jeśli deklaruje śledzenie albo zbierane dane, twoja karta w App Store i polityka prywatności muszą mówić to samo. Rozbieżność między tymi trzema może potem kosztować odrzucenie przez człowieka na podstawie guideline 5.1.1. Co trzeba ujednolicić, opisuję w artykule polityka prywatności aplikacji.
Wyślij build bez przykrych niespodzianek
Błędy ITMS naprawia się szybko, kiedy umiesz je czytać. Odrzucenia, które przychodzą później, wymagają więcej dystansu: sposób postępowania jest na stronie aplikacja odrzucona przez Apple, a najczęstsze powody w artykule dlaczego Apple odrzuca aplikację.
Żeby sprawdzić wszystko przed wysyłką, Apple Review Kit łączy moją checklistę i skille Claude Code, które przeglądają twój projekt. Jeśli wysyłka się blokuje i chcesz, żeby spojrzał na to człowiek, zarezerwuj ze mną godzinę: przejdziemy przez twój projekt na wideorozmowie, na twoim ekranie. A jeśli przygotowujesz swoje pierwsze wydanie, zrób darmową diagnozę: ułoży kroki we właściwej kolejności i wyśle ci moją checklistę przeciw odrzuceniom Apple.
Najczęściej zadawane pytania
Czy ITMS-91053 blokuje wysyłkę mojego builda?
Apple pisze, że od 1 maja 2024 App Store Connect nie przyjmuje aplikacji, które nie opisują użycia tych API. Niektórzy deweloperzy dostali jednak ten mail przy przyjętym buildzie, jako ostrzeżenie. W obu przypadkach popraw manifest przed kolejnym buildem.
Jaki kod uzasadnienia wybrać dla UserDefaults?
CA92.1, jeśli aplikacja czyta i zapisuje tylko własne preferencje. Dodaj 1C8F.1, jeśli współdzieli je z widgetem lub rozszerzeniem przez App Group. C56D.1 jest zarezerwowany dla zewnętrznych SDK: nie wpisuj go do manifestu swojej aplikacji.
Czy trzeba przepisać manifest każdego SDK do app.json?
Niekoniecznie. Jeśli scalanie manifestów jest włączone, manifesty zależności CocoaPods są łączone podczas instalacji podów. Przepisz kategorię tylko wtedy, gdy brakuje jej w finalnym manifeście, z uzasadnieniem opisującym faktyczne użycie.
Czy mój widget lub rozszerzenie potrzebuje własnego manifestu?
Według Apple każdy bundle zawierający plik wykonywalny, który używa tych API, musi mieć własny manifest. Rozszerzenie to osobny plik wykonywalny we własnym bundle'u: manifest aplikacji go nie obejmuje.
Czy można naprawić ITMS-91053 bez nowego builda?
Nie. Manifest prywatności jest częścią binarki. Popraw konfigurację, zrób nowy build, a potem przetestuj go w TestFlight, zanim wyślesz go do review.