Pular para o conteúdo
Development

Construindo uma interface para dados que ainda não consigo ver (agrupando por álbum uma playlist do MusicKit)

Por Victor Da Luz
iosswiftmusickitdev-logdeep-cut-atlas

Esse app foi renomeado depois para Deep Cut Atlas. Abaixo ele aparece como “Discoverer”, porque era esse o nome no dia em que isso aconteceu.

Eis uma situação estranha: construí uma tela inteira para o meu app de Apple Music essa semana, rodei, e olhei para quatro álbuns que não estão na minha biblioteca. Eles são falsos. Não consigo ver minha playlist real “To Check Out” nessa máquina de jeito nenhum, o MusicKit não roda no simulador, e a versão de verdade precisa de uma conta de desenvolvedor paga que eu ainda não configurei. Então a pergunta que pairou sobre a tarefa inteira foi: como sei que a tela está certa quando não consigo apontar ela para dados reais?

A resposta acabou sendo as disciplinas chatas, feitas de propósito: uma fonte de dados falsa que eu controlo, e testes que fixam o comportamento. Deixa eu passar por isso, porque a modelagem do MusicKit tinha algumas arestas afiadas que eu não esperava.

Uma playlist é uma lista plana. Eu quero álbuns.

A aba é centrada em álbuns. Você salvou algumas faixas para conferir depois, e eu quero mostrar elas agrupadas em cards de álbum, capa, título, artista, ano, e uma etiquetazinha dizendo se é álbum, EP, single ou coletânea. Mas uma playlist do MusicKit não é álbuns. É uma lista plana de faixas. Então o passo um é agrupar.

Isso parece trivial até você olhar o que uma faixa de playlist realmente carrega. O Track do MusicKit é um enum, .song ou .musicVideo, mas ele te dá propriedades de passagem convenientes para você não precisar desembrulhar o case toda vez:

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

Você recebe track.title, track.artistName, track.albumTitle, track.artwork. O que você não recebe é a data de lançamento do álbum ou o tipo dele. Isso mora em Album, não em Track. Para preencher direito eu teria que resolver cada álbum a partir do catálogo, uma requisição de rede por álbum, e a API do Apple Music vai limitar sua taxa se você disparar isso num loop. Para uma playlist pequena tudo bem; anotei isso e segui em frente em vez de superengenheirar uma camada de lote que ainda não preciso.

(Detalhe do Swift que eu gostei: detailed.tracks ?? [] compila mesmo que tracks seja um MusicItemCollection, não um array. A coleção é ExpressibleByArrayLiteral, então o literal vazio vira uma coleção vazia. Tratamento de nil de graça, gostei.)

O MusicKit não vai te contar o que é um EP

A etiqueta precisa de quatro categorias. O MusicKit te dá dois booleanos em Album: isSingle e isCompilation. Só isso. Não existe isEP. A Apple simplesmente não expõe isso.

Então detectar EP é um chute:

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

Estou apostando na convenção de que títulos de EP costumam terminar em ”- EP”. É uma heurística, e anotei como algo para validar num dispositivo real, porque genuinamente ainda não sei o quão consistente a Apple marca isso. Prefiro lançar um chute honesto com um TODO do que fingir que resolvi.

O mock é a tela que eu realmente olho

Como nada disso roda no simulador, a superfície de desenvolvimento é um serviço mock que retorna álbuns fixos, um de cada tipo, de propósito, para que os chips de filtro e as etiquetas tenham algo para renderizar. O serviço real e o mock satisfazem o mesmo protocolo, e o app escolhe entre eles na hora da build. A view não sabe com qual dos dois está falando.

Foi isso que me deixou construir tudo hoje. A screenshot que ficou pronta mostra Mordechai (Album), Texas Sun (EP), Numb (Single), e uma coletânea da Bonobo, cada um com a etiqueta colorida certa, na ordem da playlist. Nada disso é real. Tudo isso prova que o layout, o agrupamento, a ordenação e o filtro funcionam.

O teste que provou seu valor

Eu tinha escrito cinco testes novos para o view model: carregar, o estado de playlist ausente, o filtro, remover, criar. Eles passaram. Aí a suíte inteira ficou vermelha num teste que eu tinha escrito na semana passada.

Eu tinha aumentado a playlist mock de 3 faixas para 5 para ter álbuns suficientes para a demonstração. Um teste mais antigo ainda afirmava trackCount == 3. A falha estava correta, eu tinha mudado dados de fixture compartilhados e uma asserção anterior agora era mentira. Trinta segundos para corrigir, mas é todo o argumento para rodar a suíte inteira em vez de só os seus testes novos: dados mock compartilhados são uma dependência, e mudar eles se propaga.

Com o que estou ficando

Parece estranho “terminar” uma funcionalidade que você nunca viu funcionar com dados reais. Mas acho que a divisão é honesta: a lógica e o layout estão provados contra dados que eu controlo, e a parte que ainda não posso provar, se a minha playlist real lê certo, se os EPs são marcados direito, está anotada como uma tarefa de verificação em dispositivo esperando por uma conta de desenvolvedor. O mock não é um substituto para testar. É a coisa que permitiu a funcionalidade existir antes do hardware.

Leitura relacionada

Development

A playlist que já tinha o nome certo

Um artefato de renomeação, uma API sem campo de autor pra atualizar, e um teste em dispositivo real provando que o bug já tinha se corrigido sozinho, fechado como aceitar como está.

Ler

Você também pode achar útil

Proton

Proton Mail

E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Proton

Proton VPN

VPN comercial com filtragem NetShield e interruptor de desligamento automático.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Airalo

eSIM Airalo

eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.

Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.

Saiba mais