Apple rechazó emap por un flag de audio que no usaba
Guideline 2.5.4. El revisor, en un iPad Air M4, no encontró audio persistente en segundo plano. Tenía razón: emap no es un reproductor. El flag lo había metido expo-audio por defecto.
El 13 de agosto Apple devolvió emap. No por el mapa ni por Euskadi. Guideline 2.5.4, Performance, Software Requirements. El revisor lo miró en un iPad Air de 11 pulgadas (M4) y no encontró audio persistente al pasar la app a segundo plano.
Tenía razón.
Lo que el IPA declaraba
emap no es un reproductor. La locución de navegación es TTS breve e intermitente. El modo de fondo que la sostiene es location, no audio.
En el Info.plist, aun así, iba UIBackgroundModes: audio. Lo metió expo-audio con enableBackgroundPlayback: true por defecto, más una lista explícita en app.json. El ducking de Spotify o de la radio (bajar el volumen un segundo para decir «gira a la derecha») no es reproducción en segundo plano. Apple no distingue intenciones. Distingue flags.
Responder en el Resolution Center no basta. El plist viaja en el IPA. Hacía falta un binario nuevo: EAS production, build 11. El 10 ya estaba quemado.
Los hermanos que Apple no señaló
Apple solo escribió audio. El mismo IPA 10 llevaba más declaraciones huérfanas del mismo tipo. Las abrí con npx expo config --type introspect y las quité antes del siguiente envío.
| En el IPA 10 | ¿La app lo usa? | Riesgo |
|---|---|---|
UIBackgroundModes: audio | TTS intermitente, no persistente | 2.5.4 (este rechazo) |
UIBackgroundModes: fetch | no: los tasks son de location | 2.5.4 siguiente |
NSMicrophoneUsageDescription genérico | no se graba | 5.1.1 |
NSMotionUsageDescription | no hay Core Motion | 5.1.1 |
aps-environment: production | no hay push remoto | 2.5.4 / push sin uso |
location se queda: Always se pide al iniciar navegación o el aviso de bajada. fetch salió. El micrófono salió. El entitlement de push salió: emap solo usa avisos locales y Live Activity.
El arreglo en el working tree: quitar audio de UIBackgroundModes, enableBackgroundPlayback: false, sin micrófono ni RECORD_AUDIO, y shouldPlayInBackground: false en voice.js. Tras el introspect, UIBackgroundModes quedó en [location, fetch]. fetch también salió, porque los tasks son de ubicación.
En julio ya me había pasado con el introspect: parte del ios/ local, un prebuild de dev. Sirve para ver qué mods aplican. No sirve para saber qué hay en tienda. Para eso hay que abrir el .ipa. El Info.plist del build 3 sí llevaba strings genéricos de localización y motion que el introspect y el IPA no contaban igual.
Lo que no se sostiene
En este caso el revisor describió exactamente lo que el binario declaraba. No fue una review aleatoria. El fallo era mío: un default de librería que no correspondía al producto.
El TTS en segundo plano no está prohibido. Lo que no se declara es audio si no hay reproducción persistente. El ducking se mantiene sin ese flag.
CarPlay es otra superficie y otra decisión (D-022). El rechazo del 13 de agosto no fue por el coche. Si el binario lleva CarPlay, las Notes tienen que decir iPhone y coche: el review en iPad no lo ve.
Dónde está ahora
emap en iOS: App Store.
El arreglo del 2.5.4 no es un párrafo en el Resolution Center. Es un IPA sin audio en UIBackgroundModes.