Daniel Bonates, problem solver & iOS guy at Peixe Urbano. Minhas paixões digitais são design e desenvovimento de apps para a plataforma Apple. Vivo dessas coisas desde 1998 e meu primeiro app foi para o MacOS 9, em 1991, um player de audio ;) Fiquei desviado produzindo backend e frontend para web, até que a chegada do iPhone me salvou mais uma vez, quando então comprei um Mac e voltei a ser feliz, hoje focado em MacOS, iOS e derivados!
Disclaimer 1
: Estarei tratando aqui do assunto Enum usado Swift.
Introdução
Sempre que falo de Enums, o faço com um sentimento de gratidão por um recurso que já fez muito por mim e pelo código que escrevo. Antes dele, era quase tudo literal, sabe…frágeis strings e constantes para todo lado! Nesse artigo enfrentarei 3 desafios: passar conteúdo útil, ser pragmático e objetivo, e testar minha capacidade de síntese escrevendo o mínimo de blábláblá possível.
Sobre a minha querida Objective-C:
mas voltando ao assunto…
Enum fundamental
Começando do começo, usamos enums para enumerar casos possíveis (ou se preferir, cases ou ainda, statements):
Considere essa estrutura:
Essa struct possui um título que provavelmente é único. Mas será que podemos dizer o mesmo do tipo? Vejamos o caso desse array:
Repetição, margem de erro, retardamento no entendimento de todos os tipos… tudo isso eu sou capaz de sofrer. Então, que tal enumerar os tipos de ofertas? Sugestão:
Daí nossa struct ficaria ao invés de usar String em tipo, usa nosso enum DescontoTipo:
daí o nosso array de ofertas fica assim:
Simples, efetivo e strongly typed! Sempre que formos criar uma Oferta
, o compilador não vai aceitar nada que seja diferente de DescontoTipo na propriedade tipo, prevenindo erros, entre outras vantagens que já citei.
Reforçando tipos e legibilidade
Enums também podem tornar seu código muito mais legível e flexível. Por exemplo, e se nossa Oferta
possuir um status de disponibilidade? Talvez o nosso primeiro impulso seria o de criar uma propriedade do tipo Bool:
É ok trabalhar aqui com true/false. Vai dar conta de boa, certo?! Depende!
Fazendo isso você acaba de condenar a coitada da Oferta
a estar disponível ou não, apenas 2 estados! Quer ver? E se a turma de produto chega depois e diz que a oferta pode estar disponível, indisponível, suspensa, encerrada, etc?
Veja como o uso de um enum no lugar de Bool, vai te dar muito mais flexibilidade:
Nossa Oferta
fica assim:
daí para criar uma uma oferta:
nice 😊!
Acredito que até aqui, já deu pra ver que usar enums é uma obrigação se você pretende manter seu código legível e eficiente. Então vamos agora a um case bem mais comum e pragmático.
Enum na rota, um caso mais “PRO”
Diz-se que em Swift enums tem super poderes. E com razão. São value types, podem ter extensions, variaveis computadas, receber parametros, ter metodos etc. Vamos explorar algumas dessas ferramentas e mostrar como isso pode ser bom pra gente:
A partir daqui:
Nosso cenário: vamos simular que nosso app consome uma API da web, que possui várias rotas e versões.
Nossa missão: criar um enum
Router
para gerenciar todas as rotas do nosso app.
Disclaimer 2
: Sobre a ‘sacada’ que vamos abordar:
Embora a solução final que vou apresentar aqui não seja de minha autoria, acho que vale como caso real pois foi justamente num app super complexo de ecommerce que a vi implementada. Achei uma solução sensacional assim que vi, e a partir daí adotei (com pequenas modificações) em todo app que trabalho. O autor dessa sacada genial foi o Cassius Pacheco que havia deixado sua marca no app pouco antes de eu chegar na empresa. Vamos destrinchar passo a passo essa solução pra entender como ele conseguiu resolver um sistema complexo de rotas de uma API explorando todo poder de um Enum em Swift.
Mas vamos começar devagar, com uma versão bem simples da API e vamos aumentando a complexidade para ver até onde o uso de um enum pode nos ajudar.
Nossa primeira versão da API é https://bonates.com/usuarios
, que retorna um json com a lista dos usuários.
Essa é a func
que usaremos para testar a construção da nossa rota:
Para a primeira versão da API, esse código dá conta de fazer o download dos dados:
Usei force unwrap aqui! Relaxa que já vai passar!
Isso pro seu teste num Playground tá ok, mas num app, consumindo uma API de verdade, uma url só não é nossa realidade, confere?
E tem mais! Agora precisamos acessar agora uma nova rota da nossa api que retorna a lista de ofertas: https://bonates.com/ofertas
. Bastaria criar uma nova e fedorenta variavel ofertasURL
, certo? Não! Vamos criar nosso enum para começar a cuidar das rotas:
Daí basta usar nosso Router pra buscar a url da vez:
Pronto, de novo nos livramos de literais e quem olhar essa linha de código vai saber que estamos recuperando uma url relacionada a usuarios. Bem mais profissional, não?! (Eu sei, mas ainda tem a exclamação do mal ali, calma, já disse)
Mas isso é muito pouco perto do que ainda podemos fazer pra esse código ficar bom. E tem mais uma novidade: nossa api retorna também ofertas favoritas do usuário logado. O link é esse: https://bonates.com/ofertas/favoritas
Enums aceitam variáveis computadas, então, ao invés de criar outro case favoritas
e retornar toda a url inteira, podemos torna-la mais dinâmica gerando-a a partir de seus componentes. Isso fica assim:
Nosso Router
agora nos retorna um request pronto, bastando pra isso dizer qual request queremos, inclusive com a ajuda do auto complete para os mais preguiçosos, me included o/
So far, so good ;) Olha como agora um simples if-let resolve nossos problemas de request válido e uso de force unwrap. No código a seguir, o request vai me retornar https://bonates.com/ofertas/favoritas
lindamente, safe and sound!
Passando parametros e query strings
Nossa api agora tem um recurso que lista apenas os usuários ativos: https://bonates.com/usuarios?status=ativos
. Daí, evoluindo nosso enum:
1 - mudamos o case .usuarios
para aceitar parametros:
2 - adicionamos uma variável computada:
3 - E fazemos uso dela na geração do request:
Agora qualquer quantidade de parametros passados como String:Any
vai ser aceito e entrar como query string:
Metodo HTTP
Perto do que já fizemos, incluir a capacidade de atribuir um método http de acordo com o caso acaba sendo trivial. Basta adicionar uma variável no Router com essa finalidade:
Só deixei o GET
implementado como case padrão. Incrementar um POST, DELETE, etc
é com você!
O Router até aqui ficou assim:
Pronto! Cobrimos um caso real explorando os recursos de enumeração disponíveis em Swift para criarmos um sistema de rotas fácil de escalar e fazer manutenção. Tem mais possibilidades, mas basicamente elas seriam uma combinação do que já fizemos aqui ;)
Enums + Generics - o poder não tem limites!
E pra fechar, você sabia que uma variável optional na real implementa o enum Optional da biblioteca padrão do Swift?
ou seja, quando definimos uma variável como optional, basicamente estamos fazendo isso:
Viu só?! Enum está no coração do Swift 😊!
Usando essa mesma idéia, podemos criar por exemplo um recurso muito útil para tratar erro e sucesso em requests:
A dica aqui é sua rotina retornar um enum RequestResult, verificando o resultado da operação:
Fica fácil, né?!
Considerações finais
Bem, esses são os meus argumentos pra te convencer de que enumerar seus dados usando os super poderes da nossa linguagem do coração, vai deixar seu código lindo, seus coleguinhas vão curtir quando precisarem dar manutenção e provavelmente o Crashlytics vai dar uma relaxada porque você não tipou errado! Espero ter contribuído com algumas idéias e uma inspiração a mais para tornar seu código mais seguro, organizado e fácil de ler.
Dúvidas, comentários, whatever, pode tacar por aqui, me parar na rua, ou me procurar lá no Slack da comunidade iOS, onde estou sempre presente, ainda que apenas invisível só acompanhando as tretas!
Abraços e até logo!