Construindo uma interface para dados que ainda não consigo ver (agrupando por álbum uma playlist do MusicKit)
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
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á.
Álbuns em destaque, dados reais do MusicKit, e um teste que mentiu sobre estar passando
Não existe uma flag isEssential - o Essentials que você vê é uma playlist de músicas. featuredAlbums é o sinal real, e uma mudança de fixture expôs um teste que ninguém tinha rodado de novo.
A aba Discover que ficou permanentemente presa cavando
Uma busca de artista que falhava nunca contava como checada, então um artista ruim se reselecionava para sempre - até um teste de várias rodadas achar a segunda proteção.
Você também pode achar útil
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 maisProton 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 maiseSIM 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