Un rechazo de App Store, una trampa de MusicKit, y un trabajo que creí haber perdido
Deep Cut Atlas recibió su primer rechazo de App Store esta semana, y se convirtió en una lección de dos partes: una sobre la superficie de API de MusicKit, y otra sobre mi propia higiene con git.
El rechazo
El revisor de Apple se topó con dos errores. Primero, tanto History como Discover mostraban el texto crudo “Privacy acknowledgement required” en vez de cualquier interfaz real. Segundo, tocar “Unlock lifetime Pro” en Settings lanzaba una alerta de error.
La causa raíz del primer error es una distinción en la que no había pensado lo suficiente: el estado de autorización de MusicKit solo indica si el usuario le dio permiso a la app para tocar su biblioteca. No dice nada sobre si su cuenta en realidad tiene una suscripción activa de Apple Music. Mi app validaba solo por autorización y se detenía ahí, así que un revisor que concedió acceso pero no tenía suscripción pasaba directo por esa puerta y después se topaba con un error crudo de MusicKit en la siguientísima solicitud. La solución es una segunda verificación, separada, vía MusicSubscription.current, con su propia pantalla dedicada en vez de tres pestañas mostrando cada una su propia versión del mismo problema de fondo.
El segundo error era más simple: el botón de desbloqueo Pro seguía siendo tocable incluso cuando el producto de compra dentro de la app todavía no había cargado, así que un producto nil significaba una alerta de error inevitable al tocarlo. Solución directa, ocultar el botón hasta que el producto esté realmente ahí.
Acertar con el tipo de error correcto
El texto de error específico que vio el revisor de Apple (“Privacy acknowledgement required”) viene de un caso de error real de Swift, pero adivinar su tipo exacto de memoria fue mala idea. Estaba bastante seguro de que vivía en MusicSubscription.Error, que resulta no ser un tipo real en absoluto, o al menos no uno que pude encontrar documentado. Después de un par de búsquedas confirmé que el tipo real es MusicTokenRequestError.privacyAcknowledgementRequired. Valieron la pena los cinco minutos extra: lanzar una cláusula catch que coincide con un caso que no existe significa que nunca se dispara, en silencio, y no arreglaste nada.
La parte donde creí que el trabajo se había perdido
Esta es la parte de la que estoy menos orgulloso. Una sesión de trabajo anterior aparentemente ya había construido esta misma solución, “completa, verificada en el simulador,” dejada como una nota sin confirmar en el ticket de seguimiento. Cuando volví a retomar el trabajo, busqué con grep en todo el código y en cada rama las clases que describía ese comentario. Nada. Concluí que el trabajo estaba perdido, cambios sin confirmar que nunca se guardaron antes de cambiar de rama, y reconstruí todo desde las notas del ticket.
No estaba perdido. Estaba guardado en git stash, algo que nunca se me ocurrió revisar. Un stash no aparece en git branch, no aparece en git log, y un grep sobre el árbol de trabajo tampoco puede verlo, así que mi búsqueda tenía un punto ciego real que no conocía, hasta que me topé con él más tarde, haciendo una limpieza de ramas sin relación.
El lado positivo: al comparar las dos versiones con diff, mi reconstrucción detectó un error real que tenía la original, ese tipo MusicSubscription.Error incorrecto. Así que el trabajo redundante no fue en vano. Pero “irrecuperable” fue una afirmación que hice con más confianza de la que en realidad me había ganado, y quiero recordar eso la próxima vez que algo parezca perdido.
Qué sigue
La solución de código está terminada, probada, y fusionada. Lo que queda depende enteramente de mí: confirmar en App Store Connect que la compra dentro de la app en verdad está adjunta al envío de la versión (probablemente todo el segundo error), después subir el número de build y reenviar.
Lecturas relacionadas
Las capturas de pantalla de la App Store que nadie actualizó en dos semanas
Dos carpetas que parecen intercambiables y no lo son, un caché JSON que sobrevive la reinstalación, y tres rondas de 'parece terminado' que no lo estaban.
La ventana espejada mintió sobre que el teléfono estaba desbloqueado
Un arreglo de agrupación de una línea que ya estaba escrito, y tres muros entre eso y la prueba: aislamiento de actores, el alcance exclusivo a Mac de log stream, y una sesión espejada que parece desbloqueada cuando el dispositivo no lo está.
Álbumes de preestreno, y la solución que no pude verificar del todo
Los marcadores de posición 'Track N' de Apple se mostraban como si fueran datos reales. La única señal de detección que no pude confirmar se convirtió en la única señal de la que dejé de depender.