Saltar al contenido
Development

La ventana espejada mintió sobre que el teléfono estaba desbloqueado

Por Victor Da Luz
swiftmusickitiosdev-logdeep-cut-atlas

Tomé un bug de la pestaña Playlist donde un álbum con muchas colaboraciones se renderizaba como seis filas separadas de una sola canción en vez de un solo álbum. El arreglo de código en realidad ya llevaba un día guardado en un git stash: el código de agrupación usaba como clave el string completo de artistName por canción, así que “Bad Bunny & Daddy Yankee” y “Bad Bunny & Yaviah” generaban cada uno su propio grupo. El arreglo reduce eso a un artista principal antes de agrupar. El código estaba listo, 199 pruebas pasaban. Lo que faltaba era la prueba de que funcionaba de verdad contra datos reales de MusicKit en un dispositivo real, MusicKit no corre en el simulador en absoluto.

Esa pasada por el dispositivo se convirtió en su propia pequeña investigación.

Sacar el stash y chocar con un muro de aislamiento de actores

Antes de tocar el dispositivo, quería ver los strings crudos de artistName que realmente devuelve MusicKit. Confirmar visualmente que “el álbum ahora se muestra como una sola fila” no prueba que la lista de separadores (" & ", ", ", " feat. ") esté completa, solo que funciona para los formatos ya contemplados. Así que agregué una línea de debug temporal para volcar el string crudo de artista de cada canción antes de recortarlo.

El primer instinto fue recurrir al Logger.service existente del proyecto. Mala decisión, el proyecto define SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, así que todo tipo queda implícitamente anclado al actor principal a menos que se marque lo contrario. La función de agrupación es nonisolated (por diseño, es una transformación de datos pura), pero Logger.service es un static global que hereda el aislamiento implícito a MainActor del archivo. Llamarlo desde código nonisolated no compila. Es exactamente el mismo tipo de bug que un issue anterior tuvo y resolvió marcando su extensión de Logger como nonisolated, salvo que ese arreglo vive en la rama sin fusionar de ese propio issue. Arreglo más simple: saltarse por completo el static compartido y construir una instancia local de Logger en línea. Una instancia simple no tiene afinidad de actor que heredar, así que simplemente funciona.

Después: log stream no funciona con dispositivos remotos

Con la línea de debug lista, compilé para dispositivo, y fui a usar log stream --predicate '...' para ver la salida en vivo, exactamente el patrón que una sesión anterior había planeado usar. Solo que log stream --help no tiene ninguna opción --device/--udid. Es exclusivo de Mac local, no puede transmitir remotamente el log unificado de un dispositivo iOS físico. (También apareció una trampa tonta de la shell en el camino: en esta configuración de zsh, log resuelve a un builtin de la shell, no a /usr/bin/log, así que correr la herramienta real de Apple necesita la ruta completa, o aparece un desconcertante error de “demasiados argumentos” que no tiene nada que ver con el comando real.)

Cambié la línea de debug temporal de Logger a un print() simple y relancé con xcrun devicectl device process launch --console <bundle-id> > out.txt 2>&1 &, redirigido a un archivo, leído unos segundos después. Eso funcionó de forma limpia, capturó cada string crudo de artistName de toda la playlist. Vale la pena señalar: una nota existente de la base de conocimiento de hace un par de semanas afirmaba que --console “no captura de forma confiable el stdout de la app”. Ese hallazgo ya no se sostiene, casi con certeza fue un problema de sincronización (revisar el stream antes de que hubiera corrido el código relevante), no una falla real de captura. Corregí la nota en vez de dejar que siguiera desviando mal a sesiones futuras.

Después: la pantalla del teléfono también mentía

El primer intento de lanzamiento falló con “Transition request declined: Lock screen handled request”, aunque devicectl list devices mostraba el teléfono como connected. El mismo muro de antes. Lo interesante de este caso: iPhone Mirroring estaba abierto en la Mac y una captura de pantalla mostraba la app Settings completamente renderizada e interactiva. Parecía desbloqueado. No lo estaba. iPhone Mirroring puede mostrar una sesión virtual en vivo del lado de la Mac mientras la pantalla real del dispositivo físico está apagada y bloqueada, la ventana espejada no es prueba del estado real del dispositivo. Solo un toque literal sobre el hardware real lo resolvió. Ni el estado de conexión de devicectl ni una captura de la sesión espejada pueden indicar eso, hace falta que una mano toque físicamente el teléfono.

Una pequeña complicación extra: la propia herramienta de mirroring también estaba desactualizada, su llamada status decía “not running” mientras el proceso de la app de mirroring realmente sí estaba corriendo (confirmado vía AppleScript a nivel del sistema operativo). Este es un bug ya conocido y ya documentado; el arreglo es forzar una reconexión MCP, no cerrar y reabrir la app de mirroring.

Lo que mostraron realmente los datos

Una vez superados ambos muros: el álbum se renderizó como exactamente una fila, artista correcto, 10 canciones. Y los strings crudos capturados confirmaron por qué, cada crédito de ese álbum (y de otro álbum con muchas colaboraciones que revisé junto a este) usaba únicamente los separadores " & " y ", ". Ninguno tocó los formatos más riesgosos que me preocupaban de entrada, un paréntesis "(feat. X)" o un crédito estilo latino "Artista x Artista", ambos plausibles para este catálogo en particular y ambos sin manejar en la lista de separadores actual. Eso no garantiza que esos formatos nunca aparezcan en ningún lugar del catálogo de Apple Music, pero sí cierra el chequeo específico que pedían los propios criterios de terminado de este issue: confirmar el formato real del álbum mencionado en el reporte del bug.

En resumen: el arreglo de código no necesitó ningún cambio. Lo que en realidad tomó tiempo fue probar que era seguro publicarlo, y en el camino, encontrar y corregir dos creencias desactualizadas sobre las herramientas de verificación en dispositivo antes de que también desviaran a la próxima sesión.

Lecturas relacionadas