ITMS-91053 oplossen: Missing API declaration met Expo
Door Mehdi Azizi
Gepubliceerd op , 8 min
ITMS-91053: je build gebruikt een required reason API zonder die te declareren. Zet de categorie en de code in ios.privacyManifests en maak een nieuwe build.
ITMS-91053 is een automatische controle van Apple die je per e-mail krijgt na het uploaden van je build. Het betekent dat de code van je app een "required reason" API gebruikt (voorkeuren, bestandsdatums, tijd sinds het opstarten, schijfruimte of toetsenborden) zonder in het privacymanifest uit te leggen waarom. Met Expo los je dit op in ios.privacyManifests: de categorie die Apple noemt, de code die je echte gebruik beschrijft, en daarna een nieuwe build.
In Solys, mijn wekker met minigames, staan deze redenen in de configuratie van de app. Hier zie je de echte declaratie, het commando dat laat zien wat je dependencies gebruiken, en de controle die je op het uiteindelijke bestand doet voordat je het verstuurt.
Wat betekent de foutmelding ITMS-91053?
Het bericht heet "ITMS-91053: Missing API declaration". Het zegt dat de code in een specifiek bestand van je build verwijst naar een of meer API's waarvoor je een reden moet opgeven. Daarna volgen de betrokken categorieën, bijvoorbeeld NSPrivacyAccessedAPICategoryUserDefaults.
Deze API's vragen de gebruiker nergens om toestemming. Maar je kunt ze gebruiken om een apparaat te herkennen, wat Apple fingerprinting noemt. Dat is verboden, ook als de gebruiker tracking toestaat. Apple legt het uit op de pagina over required reason API's: elke app en elke SDK moet aangeven waarom hij ze gebruikt.
Op dezelfde pagina staat de regel: sinds 1 mei 2024 accepteert App Store Connect geen apps meer die dit gebruik niet in hun manifest beschrijven. Sommige developers kregen deze e-mail als gewone waarschuwing, bij een build die wel werd geaccepteerd. Reken daar niet op: behandel het als een blokkade en los het op vóór je volgende upload.
Net als bij de andere ITMS-fouten is dit geen afwijzing door een reviewer. Niemand heeft je app geopend: Apple heeft de binary geanalyseerd.
Het iOS-privacymanifest: het bestand PrivacyInfo.xcprivacy
Het privacymanifest is een bestand met de naam PrivacyInfo.xcprivacy, in de root van de app-bundle. Volgens de documentatie van Apple over privacy manifests beschrijft het de verzamelde gegevens, tracking en de required reason API's. Die laatste komen in de array NSPrivacyAccessedAPITypes: één item per categorie, met het type en de lijst met redenen.
De valkuil: een manifest dekt alleen zijn eigen bundle. Apple schrijft het zelf: elk uitvoerbaar bestand of elke dynamische library die deze API's gebruikt, moet een manifest in zijn bundle hebben. Het manifest van je app dekt geen ingebed framework, en een SDK kan niet op het manifest van je app leunen.
Verwar het niet met de zinnen in de Info.plist die ITMS-90683 vereist. Die ziet de gebruiker bij een toestemmingsverzoek. Het manifest wordt alleen door Apple gelezen.
De 5 API-categorieën en hun redencodes
De volledige naam van een categorie begint met NSPrivacyAccessedAPICategory. De lijst met API's en redenen vind je op de pagina van de sleutel NSPrivacyAccessedAPIType, geraadpleegd op 15 oktober 2026.
| Categorie | Typische API | Gangbare reden |
|---|---|---|
| UserDefaults | UserDefaults | CA92.1 |
| FileTimestamp | creationDate | C617.1 |
| SystemBootTime | systemUptime | 35F9.1 |
| DiskSpace | volumeAvailableCapacityKey | E174.1 |
| ActiveKeyboards | activeInputModes | 54BD.1 |
De codes die je het vaakst tegenkomt:
- CA92.1: de app leest en schrijft zijn eigen voorkeuren, die andere apps niet kunnen lezen.
- 1C8F.1: voorkeuren die je deelt met een widget of extensie in dezelfde App Group.
- C617.1: datums of grootte van bestanden in de container van de app.
- 3B52.1: bestanden die de gebruiker zelf heeft gekozen, bijvoorbeeld in een documentkiezer.
- 35F9.1: de tijd meten tussen twee gebeurtenissen in de app, of een timer laten lopen.
- E174.1: de vrije ruimte checken voordat je bestanden schrijft, met een zichtbaar effect voor de gebruiker.
- 85F4.1: de schijfruimte aan de gebruiker tonen.
Declareer de reden die past bij je echte gebruik, niet de reden die het breedst lijkt. Apple geeft aan dat deze API's en hun gegevens alleen voor de opgegeven redenen gebruikt mogen worden, en nooit voor tracking. Twee codes zijn voorbehouden aan third-party SDK's: C56D.1 en 0A2A.1. Die horen niet in het manifest van je app.
Het privacymanifest declareren in Expo
Met Expo schrijf je het bestand niet zelf. Je beschrijft het onder expo.ios.privacyManifests, in app.json of app.config, en de prebuild genereert PrivacyInfo.xcprivacy. Dat is de methode uit de documentatie van Expo over privacy manifests.
Dit is het echte fragment uit de configuratie van Solys. De sleutel bevat ook tracking en verzamelde gegevens, die heb ik hier weggelaten:
{
"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 en 35F9.1 nemen over wat React Native en de Expo-modules van de app declareren. 1C8F.1 komt van Solys zelf: de app deelt instellingen met zijn widget via een App Group.
Een valkuil om te kennen: als je ios-map gegenereerd wordt en door git genegeerd wordt, komt een PrivacyInfo.xcprivacy die je met de hand in die map zet nooit in de build terecht. EAS draait de prebuild opnieuw en genereert de map opnieuw. Ik zag het op 9 september 2026 in een van mijn projecten: het handgeschreven manifest was na een prebuild verdwenen. De declaratie moet in de configuratie staan.
Laatste punt: het manifest zit in de binary. Een update op afstand verandert het niet. Je moet een nieuwe build maken en die opnieuw versturen.
Third-party SDK's met hun eigen manifest
De meeste recente libraries leveren hun eigen manifest mee. Expo-packages die deze API's gebruiken hebben een PrivacyInfo.xcprivacy in hun ios-map, net als React Native. Het probleem zit in de manier waarop ze aan je app gelinkt worden.
De documentatie van Expo waarschuwt: Apple leest niet alle manifests van statisch gelinkte CocoaPods-dependencies goed, en Expo-packages worden statisch gelinkt. Het kan dus nodig zijn hun redenen over te nemen in de configuratie van je app.
Om dat werk te beperken voegt de instelling ios.privacyManifestAggregationEnabled van expo-build-properties de manifests van de CocoaPods-dependencies samen in één bestand. In de Podfile die Expo in mijn projecten genereert (SDK 57) staat dat samenvoegen aan, tenzij je het uitzet.
Het samenvoegen heeft een grens: het ziet alleen dependencies die een manifest meeleveren. Een oude library zonder manifest blijft onzichtbaar. Lees dus het bestandspad dat in de e-mail staat: dat vertelt je waar je moet corrigeren.
| Genoemd bestand | Waar corrigeren |
|---|---|
| Het uitvoerbare bestand van de app | ios.privacyManifests |
Een .framework | De SDK updaten |
Een .appex-extensie | Manifest van de extensie |
Controleren vóór het versturen
Expo stelt voor om na het versturen de e-mail van Apple af te wachten. Dat werkt, maar elke ronde kost je een build. Dit zijn de vier controles die ik vooraf doe.
1. Bekijk wat je dependencies declareren. Vanuit de root van het project:
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
-exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
Bij Solys vond het commando op 15 oktober 2026 13 manifests en vier categorieën: bestandsdatums, voorkeuren, tijd sinds het opstarten en schijfruimte. Elke categorie in die lijst moet terugkomen in het uiteindelijke manifest van de app, of in dat van het framework dat hem gebruikt.
2. Zoek in je eigen native code. Eigen modules, plugins, targets zoals een widget: voor die code declareert geen enkele SDK iets namens jou.
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
--include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
Pas de mappen aan je project aan. Bij Solys komt via deze zoekopdracht het delen van voorkeuren via de App Group naar boven, vandaar de code 1C8F.1.
3. Lees het gegenereerde manifest. Draai npx expo prebuild -p ios en open dan ios/<NaamVanJeApp>/PrivacyInfo.xcprivacy. Let op: dit commando kan de ios-map verwijderen en opnieuw aanmaken. Heb je daar met de hand dingen aangepast, werk dan op een kopie van het project.
4. Controleer de uiteindelijke build. Dat is het enige bewijs dat telt. Download de IPA vanaf de pagina van de build op expo.dev en draai op je Mac:
unzip -q MonApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
De tweede regel toont alle ingebedde manifests, dat van de app en die van de frameworks. De derde toont dat van de app, om te vergelijken met de lijst uit stap 1.
Wat het manifest moet zeggen in lijn met je productpagina
Het manifest staat niet op zichzelf. Als het tracking of verzamelde gegevens declareert, moeten je App Store-pagina en je privacybeleid hetzelfde zeggen. Een verschil tussen die drie kan je later een menselijke afwijzing opleveren, onder guideline 5.1.1. Wat je op elkaar moet afstemmen lees je in privacybeleid voor een app.
Je build versturen zonder nare verrassingen
ITMS-fouten los je snel op als je ze kunt lezen. De afwijzingen die daarna komen vragen meer afstand: de aanpak staat op de pagina app afgewezen door Apple, en de meest voorkomende redenen vind je in waarom Apple een app afwijst.
Om je dossier vóór het versturen te checken bundelt de Apple Review Kit mijn checklist en Claude Code-skills die je project nalopen. Loopt je upload vast en wil je dat een mens meekijkt, boek een uur met mij: we lopen je project samen door via een videocall, op jouw scherm. En bereid je je eerste release voor, doe dan de gratis diagnose: die zet de stappen op volgorde en stuurt je mijn checklist tegen afwijzingen door Apple.
Veelgestelde vragen
Blokkeert ITMS-91053 het versturen van mijn build?
Apple schrijft dat App Store Connect sinds 1 mei 2024 geen apps meer accepteert die hun gebruik van deze API's niet beschrijven. Toch kregen sommige developers de e-mail als waarschuwing bij een build die wel werd geaccepteerd. Hoe dan ook: corrigeer het manifest vóór je volgende build.
Welke redencode kies ik voor UserDefaults?
CA92.1 als de app alleen zijn eigen voorkeuren leest en schrijft. Voeg 1C8F.1 toe als hij ze via een App Group deelt met een widget of extensie. C56D.1 is voorbehouden aan third-party SDK's: zet die niet in het manifest van je app.
Moet ik het manifest van elke SDK overnemen in app.json?
Niet per se. Als het samenvoegen van manifests aanstaat, worden die van de CocoaPods-dependencies bij het installeren van de pods samengevoegd. Neem een categorie alleen over als hij ontbreekt in het uiteindelijke manifest, met de reden die het echte gebruik beschrijft.
Heeft mijn widget of extensie een eigen manifest nodig?
Volgens Apple moet elke bundle met een uitvoerbaar bestand dat deze API's gebruikt een eigen manifest hebben. Een extensie is een apart uitvoerbaar bestand in een eigen bundle: het manifest van de app dekt die niet.
Kun je ITMS-91053 oplossen zonder nieuwe build?
Nee. Het privacymanifest zit in de binary. Pas de configuratie aan, maak een nieuwe build en test die in TestFlight voordat je hem ter review instuurt.