ITMS-91053 : corriger « Missing API declaration » avec Expo
Par Mehdi Azizi
Publié le , 8 min
ITMS-91053 : ton build utilise une API à raison obligatoire sans la déclarer. Ajoute sa catégorie et son code dans ios.privacyManifests, puis refais un build.
ITMS-91053 est un contrôle automatique d'Apple, envoyé par e-mail après la livraison de ton build. Il dit que le code de ton app utilise une API « à raison obligatoire » (préférences, dates de fichiers, temps depuis le démarrage, espace disque ou claviers) sans expliquer pourquoi dans son manifeste de confidentialité. Avec Expo, la correction se fait dans ios.privacyManifests : la catégorie citée par Apple, le code qui décrit ton usage réel, puis un nouveau build.
Sur Solys, mon réveil à mini-jeux, ces raisons sont déclarées dans la configuration de l'app. Voici sa déclaration réelle, la commande qui liste ce que tes dépendances utilisent, et le contrôle à faire sur le fichier final avant l'envoi.
Que veut dire l'erreur ITMS-91053 ?
Le message s'intitule « ITMS-91053: Missing API declaration ». Il indique que le code d'un fichier précis de ton build fait référence à une ou plusieurs API qui exigent une raison. Puis il liste les catégories en cause, par exemple NSPrivacyAccessedAPICategoryUserDefaults.
Ces API ne demandent aucune autorisation à l'utilisateur. Mais elles peuvent servir à reconnaître un appareil, ce qu'Apple appelle le fingerprinting. C'est interdit, même quand l'utilisateur accepte le suivi. Apple l'explique dans sa page sur les API à raison obligatoire : chaque app et chaque SDK doit déclarer pourquoi il les utilise.
La même page fixe la règle : depuis le 1er mai 2024, App Store Connect n'accepte plus une app qui ne décrit pas ces usages dans son manifeste. Des développeurs ont rapporté cet e-mail comme un simple avertissement, sur un build accepté. Ne compte pas là-dessus : traite-le comme un blocage et corrige avant le prochain envoi.
Comme les autres erreurs ITMS, ce n'est pas le refus d'un reviewer. Personne n'a ouvert ton app : Apple a analysé le binaire.
Le privacy manifest iOS : le fichier PrivacyInfo.xcprivacy
Le manifeste de confidentialité est un fichier nommé PrivacyInfo.xcprivacy, placé à la racine du bundle de l'app. D'après la documentation d'Apple sur les privacy manifests, il décrit les données collectées, le suivi et les API à raison obligatoire. Ces dernières vont dans le tableau NSPrivacyAccessedAPITypes : une entrée par catégorie, avec son type et la liste de ses raisons.
Le point qui piège : un manifeste ne couvre que son propre bundle. Apple l'écrit : chaque exécutable ou bibliothèque dynamique qui utilise ces API doit avoir un manifeste dans son bundle. Celui de ton app ne couvre pas un framework embarqué, et un SDK ne peut pas compter sur celui de ton app.
Ne le confonds pas avec les phrases de l'Info.plist qu'exige ITMS-90683. Celles-là s'affichent à l'utilisateur dans une demande d'autorisation. Le manifeste, lui, n'est lu que par Apple.
Les 5 catégories d'API et leurs codes de raison
Le nom complet d'une catégorie commence par NSPrivacyAccessedAPICategory. La liste des API et des raisons se trouve sur la page de la clé NSPrivacyAccessedAPIType, lue le 15 octobre 2026.
| Catégorie | API typique | Raison courante |
|---|---|---|
| UserDefaults | UserDefaults | CA92.1 |
| FileTimestamp | creationDate | C617.1 |
| SystemBootTime | systemUptime | 35F9.1 |
| DiskSpace | volumeAvailableCapacityKey | E174.1 |
| ActiveKeyboards | activeInputModes | 54BD.1 |
Les codes que tu croiseras le plus :
- CA92.1 : l'app lit et écrit ses propres préférences, inaccessibles aux autres apps.
- 1C8F.1 : préférences partagées avec un widget ou une extension du même App Group.
- C617.1 : dates ou taille de fichiers rangés dans le conteneur de l'app.
- 3B52.1 : fichiers que l'utilisateur a choisis, par exemple dans un sélecteur de documents.
- 35F9.1 : mesurer le temps écoulé entre deux événements de l'app, ou faire tourner un minuteur.
- E174.1 : vérifier l'espace libre avant d'écrire des fichiers, avec un effet visible pour l'utilisateur.
- 85F4.1 : afficher l'espace disque à l'utilisateur.
Déclare la raison qui correspond à ton usage réel, pas celle qui a l'air la plus large. Apple précise que ces API et leurs données ne servent qu'aux raisons déclarées, et jamais au suivi. Deux codes sont réservés aux SDK tiers : C56D.1 et 0A2A.1. Ils n'ont pas leur place dans le manifeste de ton app.
Déclarer le privacy manifest dans Expo
Avec Expo, tu n'écris pas le fichier toi‑même. Tu le décris sous expo.ios.privacyManifests, dans app.json ou app.config, et le prebuild génère PrivacyInfo.xcprivacy. C'est la méthode de la documentation d'Expo sur les privacy manifests.
Voici l'extrait réel de la configuration de Solys. La clé contient aussi le suivi et les données collectées, retirés ici :
{
"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 et 35F9.1 reprennent ce que déclarent React Native et les modules Expo de l'app. 1C8F.1 vient de Solys elle‑même : l'app partage des réglages avec son widget par un App Group.
Un piège à connaître : si ton dossier ios est généré et ignoré par git, un PrivacyInfo.xcprivacy écrit à la main dans ce dossier n'arrive jamais dans le build. EAS relance le prebuild et régénère le dossier. Je l'ai constaté sur un de mes projets le 9 septembre 2026 : le manifeste écrit à la main avait disparu après un prebuild. La déclaration doit vivre dans la configuration.
Dernier point : le manifeste fait partie du binaire. Une mise à jour à distance ne le modifie pas. Il faut refaire un build et le renvoyer.
Les SDK tiers qui embarquent leur propre manifeste
La plupart des bibliothèques récentes fournissent leur manifeste. Les paquets Expo qui utilisent ces API ont un PrivacyInfo.xcprivacy dans leur dossier ios, tout comme React Native. Le problème vient de la façon dont ils sont liés à ton app.
La documentation d'Expo prévient : Apple ne lit pas correctement tous les manifestes des dépendances CocoaPods liées en statique, ce qui est le cas des paquets Expo. Il peut donc falloir recopier leurs raisons dans la configuration de ton app.
Pour limiter ce travail, le réglage ios.privacyManifestAggregationEnabled d'expo-build-properties fusionne les manifestes des dépendances CocoaPods dans un seul fichier. Dans le Podfile qu'Expo génère sur mes projets (SDK 57), cette fusion est active, sauf si tu la désactives.
La fusion a une limite : elle ne voit que les dépendances qui fournissent un manifeste. Une bibliothèque ancienne qui n'en a pas reste invisible. Lis donc le chemin du fichier cité dans l'e-mail : il dit où corriger.
| Fichier cité | Où corriger |
|---|---|
| L'exécutable de l'app | ios.privacyManifests |
Un .framework | Mettre à jour le SDK |
Une extension .appex | Manifeste de l'extension |
Vérifier avant l'envoi
Expo propose d'attendre l'e-mail d'Apple après un envoi. Ça marche, mais chaque aller-retour coûte un build. Voici les quatre contrôles que je fais avant.
1. Liste ce que tes dépendances déclarent. Depuis la racine du projet :
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
-exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
Sur Solys, le 15 octobre 2026, la commande trouve 13 manifestes et quatre catégories : dates de fichiers, préférences, temps depuis le démarrage et espace disque. Chaque catégorie listée doit se retrouver dans le manifeste final de l'app, ou dans celui du framework qui l'utilise.
2. Cherche dans ton propre code natif. Modules maison, plugins, cibles comme un widget : ce code-là, aucun SDK ne le déclare à ta place.
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
--include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
Adapte les dossiers à ton projet. Sur Solys, c'est cette recherche qui fait apparaître les préférences partagées par App Group, d'où le code 1C8F.1.
3. Lis le manifeste généré. Lance npx expo prebuild -p ios, puis ouvre ios/<NomDeTonApp>/PrivacyInfo.xcprivacy. Attention : cette commande peut supprimer puis recréer le dossier ios. Si tu y as fait des modifications à la main, travaille sur une copie du projet.
4. Contrôle le build final. C'est la seule preuve qui compte. Télécharge l'IPA depuis la page du build sur expo.dev, puis lance sur ton Mac :
unzip -q MonApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
La deuxième ligne liste tous les manifestes embarqués, celui de l'app et ceux des frameworks. La troisième affiche celui de l'app, à comparer avec la liste de l'étape 1.
Ce que le manifeste doit raconter avec ta fiche
Le manifeste ne vit pas seul. S'il déclare du suivi ou des données collectées, ta fiche App Store et ta politique de confidentialité doivent dire la même chose. Un écart entre les trois peut ensuite coûter un refus humain, au titre de la guideline 5.1.1. Ce qu'il faut aligner est détaillé dans politique de confidentialité d'une application.
Envoyer ton build sans mauvaise surprise
Les erreurs ITMS se corrigent vite quand on sait les lire. Les refus qui viennent ensuite demandent plus de recul : la démarche est sur la page application refusée par Apple, et les motifs les plus fréquents sont dans pourquoi Apple refuse une app.
Pour vérifier ton dossier avant l'envoi, le Kit Review Apple réunit ma checklist et des skills Claude Code qui relisent ton projet. Si ton envoi bloque et que tu veux un regard humain, réserve une heure avec moi : on reprend ton projet en visio, sur ton écran. Et si tu prépares ta première sortie, fais ton diagnostic gratuit : il remet les étapes dans l'ordre et t'envoie ma checklist anti-refus Apple.
Questions fréquentes
ITMS-91053 bloque-t-il l'envoi de mon build ?
Apple écrit que depuis le 1er mai 2024, App Store Connect n'accepte plus les apps qui ne décrivent pas leur usage de ces API. Des développeurs ont pourtant reçu l'e-mail sur un build accepté, comme un avertissement. Dans les deux cas, corrige le manifeste avant le prochain build.
Quel code de raison choisir pour UserDefaults ?
CA92.1 si l'app lit et écrit seulement ses propres préférences. Ajoute 1C8F.1 si elle les partage avec un widget ou une extension par un App Group. C56D.1 est réservé aux SDK tiers : ne le mets pas dans le manifeste de ton app.
Faut-il recopier le manifeste de chaque SDK dans app.json ?
Pas forcément. Si la fusion des manifestes est active, ceux des dépendances CocoaPods sont réunis à l'installation des pods. Recopie une catégorie seulement si elle manque dans le manifeste final, avec la raison qui décrit l'usage réel.
Mon widget ou mon extension a-t-il besoin de son propre manifeste ?
D'après Apple, chaque bundle qui contient un exécutable utilisant ces API doit avoir son propre manifeste. Une extension est un exécutable à part, dans son propre bundle : le manifeste de l'app ne la couvre pas.
Peut-on corriger ITMS-91053 sans refaire un build ?
Non. Le manifeste de confidentialité fait partie du binaire. Corrige la configuration, refais un build, puis teste-le dans TestFlight avant de l'envoyer en revue.