Pular para o conteúdo
Development

Uma seção de tracklist, e por que levou 30 minutos

Por Victor Da Luz
iosswiftdev-logdeep-cut-atlas

A feature de hoje era pequena: a sheet de lançamento da aba Discover devia mostrar as faixas do álbum, para você poder avaliar um lançamento antes de adicionar ele à sua playlist ou dispensar. O que fez valer a pena escrever sobre isso foi o quão pouco pensamento novo ela exigiu.

A mudança

A sheet precisava de dados de faixas que o app ainda não buscava em lugar nenhum. Uma nova leitura no protocolo de serviço:

func fetchTracks(forCatalogAlbum album: LibraryAlbum) async throws -> [LibraryTrack]

A implementação real é uma única requisição de catálogo com o relacionamento .tracks, mapeada através de um track mapper que já existia para o fluxo de playlist. O mock retorna fixtures por álbum e ganha um caso de injeção de falha como todo outro método, para que os testes consigam falhar exatamente essa chamada e nada mais.

O view model ganhou um loadTracks() e uma propriedade de estado. Não defini um novo enum de estado, porque a base de código já tem um exatamente para esse formato de problema: SuggestionsState (loading, loaded, empty, failed) comanda as seções “more from this artist” em outras duas sheets. Reaproveitar ele significou que o spinner de carregamento, o texto de vazio e a linha de falha-com-retry da tracklist saíram consistentes com o resto do app sem nenhuma decisão de design.

Uma decisão de roteamento que vale destacar. As duas ações já existentes dessa sheet (adicionar à playlist, não tenho interesse) delegam para o view model pai do Discover, porque elas alteram o feed e precisam ficar consistentes com ele. Já a leitura da tracklist vai direto para o serviço. Essa divisão, leituras diretas e escritas que alteram o feed passando pelo pai, é a convenção sobre a qual a sheet já tinha sido construída. Seguir ela manteve o pai intocado, exceto por uma linha no factory method.

O orçamento de lint fez o trabalho dele

A classe de serviço estava exatamente em 300 linhas de código, o limite de type-body do SwiftLint, depois do trabalho de performance. Adicionar um método de 7 linhas significava que algo teria que ceder. Os três mappers estáticos de DTO se mudaram para uma extension privada no mesmo arquivo, o que mantém o acesso private funcionando (o private do Swift é escopado por arquivo para extensions no mesmo arquivo) e deixou o corpo da classe bem abaixo do limite.

Eu costumava achar esses limites irritantes. Esse aqui forçou uma divisão que eu já devia ter feito de qualquer jeito: os mappers são funções puras que nunca fizeram parte de verdade do trabalho da classe.

Verificação

186 testes passam, três deles novos: as faixas carregam em ordem, um álbum sem tracklist mostra o estado vazio, e uma falha injetada mostra o erro e depois tem sucesso no retry. Depois a passada de sempre no dispositivo, porque o MusicKit não retorna nada no simulador: compilei para o celular via devicectl, abri alguns lançamentos, e vi tracklists reais preenchendo.

No total foram sete arquivos e 160 linhas, a maioria testes e fixtures. Quando uma feature custa tão pouco assim, geralmente não é porque a feature era trivial. É porque as últimas dez features deixaram convenções para trás nas quais essa aqui pôde se apoiar.

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 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
AdGuard

AdGuard para iOS

Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.

Como afiliado da AdGuard, ganho com compras qualificadas.

Saiba mais
Proton

Proton Drive

Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.

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

Saiba mais