Saltar al contenido
Development

Construir una interfaz para datos que todavía no puedo ver (agrupar por álbum una playlist de MusicKit)

Por Victor Da Luz
iosswiftmusickitdev-logdeep-cut-atlas

Esta app se renombró más adelante a Deep Cut Atlas. Acá abajo se la llama “Discoverer” todo el tiempo, porque así se llamaba el día en que pasó esto.

Es un lugar raro para estar: construí toda una pantalla para mi app de Apple Music esta semana, la corrí, y miré cuatro álbumes que no están en mi biblioteca. Son falsos. No puedo ver mi playlist real “To Check Out” en esta máquina de ninguna forma, MusicKit no corre en el simulador, y lo real necesita una cuenta de desarrollador paga que todavía no configuré. Así que la pregunta que sobrevoló toda la tarea fue: ¿cómo sé que la pantalla está bien cuando no puedo apuntarla a datos reales?

La respuesta resultó ser las disciplinas aburridas, hechas a propósito: una fuente de datos falsa que controlo, y pruebas que fijan el comportamiento. Repaso esto a continuación, porque el modelado de MusicKit tuvo algunas aristas filosas que no esperaba.

Una playlist es una lista plana. Quiero álbumes.

La pestaña está centrada en álbumes. Se guardaron algunas canciones para revisar después, y quiero mostrarlas agrupadas en tarjetas de álbum: arte, título, artista, año, y una pequeña insignia que indica si es álbum, EP, single o compilación. Pero una playlist de MusicKit no son álbumes. Es una lista plana de canciones. Así que el primer paso es agrupar.

Suena trivial hasta que se mira lo que realmente lleva una canción de playlist. Track de MusicKit es un enum, .song o .musicVideo, pero da propiedades de paso convenientes para no tener que desenvolver el caso cada vez:

for track in detailed.tracks ?? [] {
    let key = (track.albumTitle ?? "") + "\u{1F}" + track.artistName
    // group by key, preserving first-appearance order
}

Se obtiene track.title, track.artistName, track.albumTitle, track.artwork. Lo que no se obtiene es la fecha de lanzamiento del álbum ni su tipo. Esos viven en Album, no en Track. Para completarlos correctamente habría que resolver cada álbum desde el catálogo (una solicitud de red por álbum), y la API de Apple Music aplica límite de tasa si se disparan en un loop. Para una playlist chica eso está bien; lo anoté y seguí en vez de sobre-diseñar una capa de batching que todavía no necesito.

(Un detallito de Swift que me gustó: detailed.tracks ?? [] compila aunque tracks sea un MusicItemCollection y no un array. La colección es ExpressibleByArrayLiteral, así que el literal vacío se convierte en una colección vacía. Un buen manejo de nil, gratis.)

MusicKit no dice qué es un EP

La insignia necesita cuatro categorías. MusicKit da dos booleanos en Album: isSingle e isCompilation. Eso es todo. No existe isEP. Apple simplemente no lo expone.

Así que detectar un EP es una adivinanza:

if album.isCompilation == true { return .compilation }
if album.isSingle == true { return .single }
if album.title.localizedCaseInsensitiveContains("- EP") { return .ep }
return .album

Me apoyo en la convención de que los títulos de EP suelen terminar en ”- EP”. Es una heurística, y la anoté como algo para validar en un dispositivo real, porque genuinamente todavía no sé qué tan confiable es el etiquetado de Apple. Prefiero publicar una adivinanza honesta con un TODO antes que fingir que lo resolví.

El mock es la pantalla que realmente miro

Como nada de esto corre en el simulador, la superficie de desarrollo es un servicio simulado que devuelve álbumes fijos, uno de cada tipo, a propósito, para que los chips de filtro y las insignias tengan algo que renderizar. Tanto el servicio real como el simulado satisfacen el mismo protocolo, y la app elige entre ellos en el momento de compilación. La vista no sabe con cuál de los dos está hablando.

Eso fue lo que me permitió construir todo hoy. La captura de pantalla con la que terminé muestra Mordechai (Álbum), Texas Sun (EP), Numb (Single), y una compilación de Bonobo, cada uno con la insignia del color correcto, en el orden de la playlist. Nada de eso es real. Todo prueba que el layout, la agrupación, el orden y el filtro funcionan.

La prueba que se ganó su lugar

Había escrito cinco pruebas nuevas para el view model: carga, el estado de playlist ausente, el filtro, eliminar, crear. Pasaron. Después toda la suite se puso en rojo por una prueba que escribí la semana pasada.

Había subido la playlist simulada de 3 a 5 canciones para tener suficientes álbumes para la demo. Una prueba más vieja todavía afirmaba trackCount == 3. La falla era correcta, había cambiado datos de fixture compartidos y una afirmación anterior ahora era mentira. Treinta segundos para arreglarlo, pero es todo el argumento para correr la suite entera en vez de solo las pruebas nuevas: los datos simulados compartidos son una dependencia, y cambiarlos genera ondas.

Con lo que me quedo

Se siente al revés “terminar” una funcionalidad que nunca se vio funcionar con datos reales. Pero creo que la división es honesta: la lógica y el layout están probados contra datos que controlo, y la parte que todavía no puedo probar (si mi playlist real se lee bien, si los EP quedan etiquetados correctamente) está anotada como una tarea de verificación en dispositivo, a la espera de una cuenta de desarrollador. El mock no es un sustituto de las pruebas. Es lo que permitió que la funcionalidad existiera, antes que el hardware.

Lecturas relacionadas