ITMS-91053: corregir Missing API declaration en Expo
Por Mehdi Azizi
Publicado el , 8 min
ITMS-91053: tu build usa una API de motivo obligatorio sin declararla. Agrega su categoría y su código en ios.privacyManifests y vuelve a hacer un build.
ITMS-91053 es un control automático de Apple, que llega por correo después de la entrega de tu build. Dice que el código de tu app usa una API "de motivo obligatorio" (preferencias, fechas de archivos, tiempo desde el arranque, espacio en disco o teclados) sin explicar por qué en su manifiesto de privacidad. Con Expo, la corrección se hace en ios.privacyManifests: la categoría que cita Apple, el código que describe tu uso real y luego un nuevo build.
En Solys, mi despertador con minijuegos, estos motivos están declarados en la configuración de la app. Aquí tienes su declaración real, el comando que lista lo que usan tus dependencias y la verificación que hay que hacer sobre el archivo final antes del envío.
¿Qué significa el error ITMS-91053?
El mensaje se titula "ITMS-91053: Missing API declaration". Indica que el código de un archivo concreto de tu build hace referencia a una o varias API que exigen un motivo. Después lista las categorías implicadas, por ejemplo NSPrivacyAccessedAPICategoryUserDefaults.
Estas API no le piden ningún permiso al usuario. Pero pueden servir para reconocer un dispositivo, lo que Apple llama fingerprinting. Eso está prohibido, incluso cuando el usuario acepta el rastreo. Apple lo explica en su página sobre las API de motivo obligatorio: cada app y cada SDK debe declarar por qué las usa.
La misma página fija la regla: desde el 1 de mayo de 2024, App Store Connect ya no acepta una app que no describa estos usos en su manifiesto. Algunos desarrolladores han contado que recibieron este correo como simple advertencia, en un build aceptado. No cuentes con eso: trátalo como un bloqueo y corrige antes del próximo envío.
Como los demás errores ITMS, no es el rechazo de un revisor. Nadie abrió tu app: Apple analizó el binario.
El privacy manifest de iOS: el archivo PrivacyInfo.xcprivacy
El manifiesto de privacidad es un archivo llamado PrivacyInfo.xcprivacy, ubicado en la raíz del bundle de la app. Según la documentación de Apple sobre los privacy manifests, describe los datos recopilados, el rastreo y las API de motivo obligatorio. Estas últimas van en el arreglo NSPrivacyAccessedAPITypes: una entrada por categoría, con su tipo y la lista de sus motivos.
La trampa: un manifiesto solo cubre su propio bundle. Apple lo dice: cada ejecutable o biblioteca dinámica que use estas API debe tener un manifiesto en su bundle. El de tu app no cubre un framework incluido, y un SDK no puede depender del de tu app.
No lo confundas con las frases del Info.plist que exige ITMS-90683. Esas se le muestran al usuario en una solicitud de permiso. El manifiesto, en cambio, solo lo lee Apple.
Las 5 categorías de API y sus códigos de motivo
El nombre completo de una categoría empieza por NSPrivacyAccessedAPICategory. La lista de API y motivos está en la página de la clave NSPrivacyAccessedAPIType, consultada el 15 de octubre de 2026.
| Categoría | API típica | Motivo habitual |
|---|---|---|
| UserDefaults | UserDefaults | CA92.1 |
| FileTimestamp | creationDate | C617.1 |
| SystemBootTime | systemUptime | 35F9.1 |
| DiskSpace | volumeAvailableCapacityKey | E174.1 |
| ActiveKeyboards | activeInputModes | 54BD.1 |
Los códigos que vas a ver más seguido:
- CA92.1: la app lee y escribe sus propias preferencias, inaccesibles para otras apps.
- 1C8F.1: preferencias compartidas con un widget o una extensión del mismo App Group.
- C617.1: fechas o tamaño de archivos guardados en el contenedor de la app.
- 3B52.1: archivos que eligió el usuario, por ejemplo en un selector de documentos.
- 35F9.1: medir el tiempo transcurrido entre dos eventos de la app, o hacer funcionar un temporizador.
- E174.1: verificar el espacio libre antes de escribir archivos, con un efecto visible para el usuario.
- 85F4.1: mostrarle el espacio en disco al usuario.
Declara el motivo que corresponde a tu uso real, no el que parece más amplio. Apple precisa que estas API y sus datos solo sirven para los motivos declarados, y nunca para el rastreo. Hay dos códigos reservados para SDK de terceros: C56D.1 y 0A2A.1. No tienen lugar en el manifiesto de tu app.
Declarar el privacy manifest en Expo
Con Expo, no escribes el archivo tú mismo. Lo describes bajo expo.ios.privacyManifests, en app.json o app.config, y el prebuild genera PrivacyInfo.xcprivacy. Es el método de la documentación de Expo sobre los privacy manifests.
Este es el extracto real de la configuración de Solys. La clave también contiene el rastreo y los datos recopilados, que quité aquí:
{
"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 y 35F9.1 retoman lo que declaran React Native y los módulos Expo de la app. 1C8F.1 viene de la propia Solys: la app comparte ajustes con su widget mediante un App Group.
Una trampa que conviene conocer: si tu carpeta ios se genera y git la ignora, un PrivacyInfo.xcprivacy escrito a mano en esa carpeta nunca llega al build. EAS vuelve a lanzar el prebuild y regenera la carpeta. Lo comprobé en uno de mis proyectos el 9 de septiembre de 2026: el manifiesto escrito a mano había desaparecido después de un prebuild. La declaración tiene que vivir en la configuración.
Último punto: el manifiesto forma parte del binario. Una actualización remota no lo modifica. Hay que hacer un nuevo build y volver a enviarlo.
Los SDK de terceros que incluyen su propio manifiesto
La mayoría de las bibliotecas recientes traen su manifiesto. Los paquetes de Expo que usan estas API tienen un PrivacyInfo.xcprivacy en su carpeta ios, igual que React Native. El problema viene de cómo se enlazan a tu app.
La documentación de Expo advierte: Apple no lee correctamente todos los manifiestos de las dependencias CocoaPods enlazadas de forma estática, que es el caso de los paquetes de Expo. Así que puede que tengas que copiar sus motivos en la configuración de tu app.
Para reducir ese trabajo, el ajuste ios.privacyManifestAggregationEnabled de expo-build-properties fusiona los manifiestos de las dependencias CocoaPods en un solo archivo. En el Podfile que Expo genera en mis proyectos (SDK 57), esta fusión está activa, salvo que la desactives.
La fusión tiene un límite: solo ve las dependencias que traen un manifiesto. Una biblioteca antigua que no lo tiene sigue siendo invisible. Así que lee la ruta del archivo citado en el correo: te dice dónde corregir.
| Archivo citado | Dónde corregir |
|---|---|
| El ejecutable de la app | ios.privacyManifests |
Un .framework | Actualizar el SDK |
Una extensión .appex | Manifiesto de la extensión |
Verificar antes del envío
Expo sugiere esperar el correo de Apple después de un envío. Funciona, pero cada ida y vuelta cuesta un build. Estas son las cuatro verificaciones que hago antes.
1. Lista lo que declaran tus dependencias. Desde la raíz del proyecto:
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
-exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
En Solys, el 15 de octubre de 2026, el comando encuentra 13 manifiestos y cuatro categorías: fechas de archivos, preferencias, tiempo desde el arranque y espacio en disco. Cada categoría listada debe aparecer en el manifiesto final de la app, o en el del framework que la usa.
2. Busca en tu propio código nativo. Módulos propios, plugins, targets como un widget: ese código no lo declara ningún SDK por ti.
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
--include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
Adapta las carpetas a tu proyecto. En Solys, es esta búsqueda la que hace aparecer las preferencias compartidas por App Group, y de ahí el código 1C8F.1.
3. Lee el manifiesto generado. Ejecuta npx expo prebuild -p ios y luego abre ios/<NomDeTonApp>/PrivacyInfo.xcprivacy. Cuidado: este comando puede borrar y volver a crear la carpeta ios. Si hiciste cambios a mano ahí, trabaja sobre una copia del proyecto.
4. Revisa el build final. Es la única prueba que cuenta. Descarga el IPA desde la página del build en expo.dev y luego ejecuta en tu Mac:
unzip -q MonApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
La segunda línea lista todos los manifiestos incluidos, el de la app y los de los frameworks. La tercera muestra el de la app, para compararlo con la lista del paso 1.
Lo que el manifiesto debe contar junto con tu ficha
El manifiesto no vive solo. Si declara rastreo o datos recopilados, tu ficha de App Store y tu política de privacidad deben decir lo mismo. Una diferencia entre los tres puede costarte después un rechazo humano, por la guideline 5.1.1. Lo que hay que alinear está detallado en política de privacidad de una aplicación.
Enviar tu build sin sorpresas
Los errores ITMS se corrigen rápido cuando sabes leerlos. Los rechazos que vienen después piden más perspectiva: el proceso está en la página aplicación rechazada por Apple, y los motivos más frecuentes están en por qué Apple rechaza una app.
Para revisar tu proyecto antes del envío, el Apple Review Kit reúne mi checklist y skills de Claude Code que revisan tu proyecto. Si tu envío está bloqueado y quieres una mirada humana, reserva una hora conmigo: revisamos tu proyecto por videollamada, en tu pantalla. Y si estás preparando tu primer lanzamiento, haz tu diagnóstico gratuito: pone los pasos en orden y te envía mi checklist antirrechazos de Apple.
Preguntas frecuentes
¿ITMS-91053 bloquea el envío de mi build?
Apple escribe que desde el 1 de mayo de 2024, App Store Connect ya no acepta las apps que no describen su uso de estas API. Aun así, algunos desarrolladores recibieron el correo en un build aceptado, como advertencia. En ambos casos, corrige el manifiesto antes del próximo build.
¿Qué código de motivo elijo para UserDefaults?
CA92.1 si la app solo lee y escribe sus propias preferencias. Agrega 1C8F.1 si las comparte con un widget o una extensión mediante un App Group. C56D.1 está reservado para SDK de terceros: no lo pongas en el manifiesto de tu app.
¿Hay que copiar el manifiesto de cada SDK en app.json?
No necesariamente. Si la fusión de manifiestos está activa, los de las dependencias CocoaPods se reúnen al instalar los pods. Copia una categoría solo si falta en el manifiesto final, con el motivo que describe el uso real.
¿Mi widget o mi extensión necesita su propio manifiesto?
Según Apple, cada bundle que contiene un ejecutable que usa estas API debe tener su propio manifiesto. Una extensión es un ejecutable aparte, en su propio bundle: el manifiesto de la app no la cubre.
¿Se puede corregir ITMS-91053 sin hacer un nuevo build?
No. El manifiesto de privacidad forma parte del binario. Corrige la configuración, haz un nuevo build y pruébalo en TestFlight antes de enviarlo a revisión.