# ITMS-91053 : corriger « Missing API declaration » avec Expo

> 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.

Page originale : [ITMS-91053 : corriger « Missing API declaration » avec Expo](https://devoria.fr/blog/itms-91053-privacy-manifest-expo)
Auteur : [Mehdi Azizi](https://devoria.fr/mehdi-azizi)
Publication : 2026-10-25
Dernière révision du contenu : 2026-10-25


ITMS-91053 est un contrôle automatique d'Apple, envoyé par e-mail après [la livraison de ton build](https://devoria.fr/blog/publier-app-expo-app-store-eas). 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](https://developer.apple.com/documentation/bundleresources/describing-use-of-required-reason-api) : 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](https://developer.apple.com/documentation/bundleresources/privacy-manifest-files), 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](https://devoria.fr/blog/itms-90683-missing-purpose-string-expo). 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](https://developer.apple.com/documentation/bundleresources/app-privacy-configuration/nsprivacyaccessedapitypes/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](https://docs.expo.dev/guides/apple-privacy/).

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 :

```json
{
  "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](https://docs.expo.dev/versions/latest/sdk/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 :

```bash
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.

```bash
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 :

```bash
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](https://devoria.fr/blog/guideline-5-1-1-app-store-refus). Ce qu'il faut aligner est détaillé dans [politique de confidentialité d'une application](https://devoria.fr/blog/politique-confidentialite-app-ios).

## 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](https://devoria.fr/expertise/application-refusee-par-apple), et les motifs les plus fréquents sont dans [pourquoi Apple refuse une app](https://devoria.fr/blog/pourquoi-apple-refuse-votre-app).

Pour vérifier ton dossier avant l'envoi, le [Kit Review Apple](https://devoria.fr/kits/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](https://devoria.fr/coaching) : on reprend ton projet en visio, sur ton écran. Et si tu prépares ta première sortie, [fais ton diagnostic gratuit](https://devoria.fr/diagnostic) : 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](https://devoria.fr/blog/testflight-tester-son-app-iphone) avant de l'envoyer en revue.


## Ton projet

[Kit Review Apple](https://devoria.fr/kits/review-apple) : Tout ce que je vérifie avant d'envoyer une app à Apple, et deux skills qui font la relecture avec Claude Code. 29 €, paiement unique. Inclus : La checklist avant l'envoi ; Les refus que j'ai pris, et ce que j'ai corrigé ; Les notes pour le testeur ; Les skills de vérification. Prérequis : une app iPhone presque prête et Claude Code ou Cursor. Téléchargement immédiat après le paiement.

[Kit iOS Devoria](https://devoria.fr/kits/ios) : Le code de départ de mes apps, le parcours avec les messages à donner à Claude, et tous les kits. Claude part d'une app qui tourne déjà. 129,99 €, paiement unique. Inclus : Le code de départ de ton app ; Les styles visuels ; Le parcours pas à pas ; Kit Review Apple ; Kit Captures ; Kit ASO ; 1 an de mises à jour ; Bonus : la séance 1:1 à moitié prix. Prérequis : un Mac avec Xcode 26 ou plus, Claude Code (abonnement payant) ou Cursor, et un compte Apple Developer (99 $ par an) pour publier. Téléchargement immédiat après le paiement.

[Diagnostic gratuit](https://devoria.fr/diagnostic) : quelques questions sur ton projet. Il dit ce qui te bloque vraiment, l'ordre des quatre étapes et le kit qui correspond. En laissant ton e-mail, tu reçois la checklist anti-refus Apple : les points vérifiés avant chaque envoi, tirés des refus reçus par les apps du studio.

[Une heure avec moi](https://devoria.fr/coaching) : On regarde ton app ou ton projet Claude, écran partagé : ce qui bloque, ce que je change en premier, et dans quel ordre. 100 € la séance d'une heure, en visio, paiement en une fois. Sujets : L'audit de ton app ; Ton travail avec Claude ; Ta fiche et ton paywall ; Un refus d'Apple.
