ITMS-91053: rezolvă „Missing API declaration” cu Expo
De Mehdi Azizi
Publicat pe , 8 min.
ITMS-91053: build-ul tău folosește un API cu motiv obligatoriu fără să-l declare. Adaugă categoria și codul în ios.privacyManifests, apoi refă build-ul.
ITMS-91053 este o verificare automată de la Apple, trimisă pe e-mail după livrarea build-ului tău. Îți spune că în codul aplicației tale se folosește un API „cu motiv obligatoriu” (preferințe, datele fișierelor, timpul de la pornire, spațiul pe disc sau tastaturile) fără să explici de ce în manifestul de confidențialitate. Cu Expo, corectura se face în ios.privacyManifests: categoria citată de Apple, codul care descrie folosirea ta reală, apoi un build nou.
Pe Solys, alarma mea cu mini-jocuri, aceste motive sunt declarate în configurația aplicației. Mai jos găsești declarația ei reală, comanda care listează ce folosesc dependențele tale și verificarea de făcut pe fișierul final înainte de trimitere.
Ce înseamnă eroarea ITMS-91053?
Mesajul se intitulează „ITMS-91053: Missing API declaration”. Îți spune că codul dintr-un anumit fișier din build face referire la unul sau mai multe API-uri care cer un motiv. Apoi listează categoriile în cauză, de exemplu NSPrivacyAccessedAPICategoryUserDefaults.
Aceste API-uri nu cer nicio permisiune utilizatorului. Dar pot fi folosite ca să recunoască un dispozitiv, ceea ce Apple numește fingerprinting. Asta e interzis, chiar și când utilizatorul acceptă urmărirea. Apple explică totul în pagina despre API-urile cu motiv obligatoriu: fiecare aplicație și fiecare SDK trebuie să declare de ce le folosește.
Aceeași pagină stabilește regula: din 1 mai 2024, App Store Connect nu mai acceptă o aplicație care nu descrie aceste utilizări în manifestul ei. Unii dezvoltatori au raportat acest e-mail ca simplu avertisment, pe un build acceptat. Nu te baza pe asta: tratează-l ca pe un blocaj și corectează înainte de următoarea trimitere.
Ca celelalte erori ITMS, nu e respingerea unui reviewer. Nimeni nu ți-a deschis aplicația: Apple a analizat binarul.
Privacy manifest pe iOS: fișierul PrivacyInfo.xcprivacy
Manifestul de confidențialitate este un fișier numit PrivacyInfo.xcprivacy, pus la rădăcina bundle-ului aplicației. Conform documentației Apple despre privacy manifests, el descrie datele colectate, urmărirea și API-urile cu motiv obligatoriu. Acestea din urmă intră în tabloul NSPrivacyAccessedAPITypes: o intrare pe categorie, cu tipul ei și lista motivelor.
Capcana: un manifest acoperă doar propriul bundle. Apple o scrie clar: fiecare executabil sau bibliotecă dinamică ce folosește aceste API-uri trebuie să aibă un manifest în bundle-ul său. Cel al aplicației tale nu acoperă un framework inclus, iar un SDK nu se poate baza pe cel al aplicației tale.
Nu-l confunda cu frazele din Info.plist cerute de ITMS-90683. Acelea îi apar utilizatorului într-o cerere de permisiune. Manifestul e citit doar de Apple.
Cele 5 categorii de API și codurile lor de motiv
Numele complet al unei categorii începe cu NSPrivacyAccessedAPICategory. Lista API-urilor și a motivelor se află pe pagina cheii NSPrivacyAccessedAPIType, citită pe 15 octombrie 2026.
| Categorie | API tipic | Motiv uzual |
|---|---|---|
| UserDefaults | UserDefaults | CA92.1 |
| FileTimestamp | creationDate | C617.1 |
| SystemBootTime | systemUptime | 35F9.1 |
| DiskSpace | volumeAvailableCapacityKey | E174.1 |
| ActiveKeyboards | activeInputModes | 54BD.1 |
Codurile pe care le vei întâlni cel mai des:
- CA92.1: aplicația citește și scrie propriile preferințe, inaccesibile altor aplicații.
- 1C8F.1: preferințe partajate cu un widget sau o extensie din același App Group.
- C617.1: datele sau dimensiunea fișierelor stocate în containerul aplicației.
- 3B52.1: fișiere alese de utilizator, de exemplu într-un selector de documente.
- 35F9.1: măsurarea timpului scurs între două evenimente din aplicație sau rularea unui cronometru.
- E174.1: verificarea spațiului liber înainte de a scrie fișiere, cu efect vizibil pentru utilizator.
- 85F4.1: afișarea spațiului pe disc pentru utilizator.
Declară motivul care corespunde folosirii tale reale, nu pe cel care pare cel mai larg. Apple precizează că aceste API-uri și datele lor servesc doar motivelor declarate și niciodată urmăririi. Două coduri sunt rezervate SDK-urilor terțe: C56D.1 și 0A2A.1. N-au ce căuta în manifestul aplicației tale.
Declararea privacy manifest în Expo
Cu Expo, nu scrii fișierul tu. Îl descrii sub expo.ios.privacyManifests, în app.json sau app.config, iar prebuild-ul generează PrivacyInfo.xcprivacy. Asta e metoda din documentația Expo despre privacy manifests.
Iată extrasul real din configurația Solys. Cheia conține și urmărirea și datele colectate, scoase aici:
{
"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 reiau ce declară React Native și modulele Expo din aplicație. 1C8F.1 vine chiar de la Solys: aplicația partajează setări cu widgetul ei printr-un App Group.
O capcană de știut: dacă folderul ios e generat și ignorat de git, un PrivacyInfo.xcprivacy scris de mână în acest folder nu ajunge niciodată în build. EAS relansează prebuild-ul și regenerează folderul. Am constatat asta pe unul dintre proiectele mele pe 9 septembrie 2026: manifestul scris de mână dispăruse după un prebuild. Declarația trebuie să stea în configurație.
Ultimul punct: manifestul face parte din binar. O actualizare la distanță nu-l modifică. Trebuie să refaci un build și să-l retrimiți.
SDK-urile terțe care vin cu propriul manifest
Majoritatea bibliotecilor recente își aduc manifestul. Pachetele Expo care folosesc aceste API-uri au un PrivacyInfo.xcprivacy în folderul lor ios, la fel ca React Native. Problema vine din felul în care sunt legate de aplicația ta.
Documentația Expo avertizează: Apple nu citește corect toate manifestele dependențelor CocoaPods legate static, cum e cazul pachetelor Expo. S-ar putea deci să trebuiască să copiezi motivele lor în configurația aplicației tale.
Ca să reduci munca asta, setarea ios.privacyManifestAggregationEnabled din expo-build-properties unește manifestele dependențelor CocoaPods într-un singur fișier. În Podfile-ul generat de Expo pe proiectele mele (SDK 57), această unire e activă, dacă nu o dezactivezi.
Unirea are o limită: vede doar dependențele care au un manifest. O bibliotecă veche fără manifest rămâne invizibilă. Citește deci calea fișierului menționat în e-mail: îți spune unde să corectezi.
| Fișier menționat | Unde corectezi |
|---|---|
| Executabilul aplicației | ios.privacyManifests |
Un .framework | Actualizează SDK-ul |
O extensie .appex | Manifestul extensiei |
Verifică înainte de trimitere
Expo propune să aștepți e-mailul de la Apple după o trimitere. Merge, dar fiecare dus-întors costă un build. Iată cele patru verificări pe care le fac înainte.
1. Listează ce declară dependențele tale. Din rădăcina proiectului:
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
-exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
Pe Solys, pe 15 octombrie 2026, comanda găsește 13 manifeste și patru categorii: datele fișierelor, preferințe, timpul de la pornire și spațiul pe disc. Fiecare categorie listată trebuie să apară în manifestul final al aplicației sau în cel al framework-ului care o folosește.
2. Caută în propriul cod nativ. Module proprii, plugin-uri, target-uri ca un widget: pe acest cod, niciun SDK nu-l declară în locul tău.
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
--include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
Adaptează folderele la proiectul tău. Pe Solys, tocmai această căutare scoate la iveală preferințele partajate prin App Group, de unde și codul 1C8F.1.
3. Citește manifestul generat. Rulează npx expo prebuild -p ios, apoi deschide ios/<NumeleAplicatiei>/PrivacyInfo.xcprivacy. Atenție: comanda poate șterge și recrea folderul ios. Dacă ai făcut modificări de mână acolo, lucrează pe o copie a proiectului.
4. Verifică build-ul final. E singura dovadă care contează. Descarcă IPA-ul de pe pagina build-ului de pe expo.dev, apoi rulează pe Mac:
unzip -q MyApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
A doua linie listează toate manifestele incluse, al aplicației și ale framework-urilor. A treia îl afișează pe cel al aplicației, de comparat cu lista de la pasul 1.
Ce trebuie să spună manifestul în acord cu fișa ta
Manifestul nu trăiește singur. Dacă declară urmărire sau date colectate, fișa ta din App Store și politica de confidențialitate trebuie să spună același lucru. O diferență între cele trei te poate costa apoi o respingere umană, în baza guideline 5.1.1. Ce trebuie aliniat e detaliat în politica de confidențialitate a unei aplicații.
Trimite-ți build-ul fără surprize neplăcute
Erorile ITMS se rezolvă repede când știi să le citești. Respingerile care vin după cer mai multă distanță: pașii sunt pe pagina aplicație respinsă de Apple, iar motivele cele mai frecvente sunt în de ce respinge Apple o aplicație.
Ca să-ți verifici dosarul înainte de trimitere, Apple Review Kit adună checklist-ul meu și skills Claude Code care îți recitesc proiectul. Dacă trimiterea ți se blochează și vrei o privire umană, rezervă o oră cu mine: reluăm proiectul tău pe video, pe ecranul tău. Iar dacă îți pregătești prima lansare, fă diagnosticul gratuit: pune pașii în ordine și îți trimite checklist-ul meu anti-respingere Apple.
Întrebări frecvente
ITMS-91053 blochează trimiterea build-ului meu?
Apple scrie că din 1 mai 2024 App Store Connect nu mai acceptă aplicațiile care nu descriu folosirea acestor API-uri. Totuși, unii dezvoltatori au primit e-mailul pe un build acceptat, ca avertisment. În ambele cazuri, corectează manifestul înainte de următorul build.
Ce cod de motiv aleg pentru UserDefaults?
CA92.1 dacă aplicația citește și scrie doar propriile preferințe. Adaugă 1C8F.1 dacă le partajează cu un widget sau o extensie printr-un App Group. C56D.1 e rezervat SDK-urilor terțe: nu-l pune în manifestul aplicației tale.
Trebuie să copiez manifestul fiecărui SDK în app.json?
Nu neapărat. Dacă unirea manifestelor e activă, cele ale dependențelor CocoaPods sunt reunite la instalarea pod-urilor. Copiază o categorie doar dacă lipsește din manifestul final, cu motivul care descrie folosirea reală.
Widgetul sau extensia mea are nevoie de propriul manifest?
Conform Apple, fiecare bundle care conține un executabil ce folosește aceste API-uri trebuie să aibă propriul manifest. O extensie e un executabil separat, în propriul bundle: manifestul aplicației nu o acoperă.
Pot rezolva ITMS-91053 fără să refac build-ul?
Nu. Manifestul de confidențialitate face parte din binar. Corectează configurația, refă build-ul, apoi testează-l în TestFlight înainte să-l trimiți la review.