Enums em Swift | O Guia Completo 🇧🇷 - Raw Values, Associated Values, CaseInterable e Mais!
-
Além de propriedades, inicializadores e métodos, os enumerations também suportam chamadas recursivas, onde através dos associados de valores, nós enviamos outros enumerations.
Nós também conseguimos utilizar as extensions para estender as funcionalidades originais. E aí, meu povo, aqui quem fala é Michel Lopes. E nesse vídeo, nós vamos explorar os enumerations, que também são conhecidos como enums. É bem comum ver programadores controlando todo o fluxo de um código através da simples comparação de uma string ou através do que é conhecido como números mágicos, onde decisões importantes são tomadas com base em um número aleatório que foi escolhido sem um motivo lógico. Isso acontece porque, na elaboração de um código, eles levam em consideração apenas o cenário feliz, acreditando que a máquina sempre vai conseguir executar o que foi planejado com perfeição. Mas para garantir que um projeto esteja realmente seguro, é preciso criar validações que irão agir como uma espécie de barreira. E sem dúvida, uma das validações mais importantes em um código é barrar valores indesejados. Quanto mais possibilidades e incertezas envolvem o valor, maiores serão os riscos de uma informação indesejada causar um erro no seu código.
Imagina a imensidão de possibilidades que os valores de tipos como uma string ou inteiro podem ter. Existem situações onde você já sabe que as opções de um valor serão limitadas. E ao invés de omitir essa informação, tentando controlar manualmente cada possibilidade de um valor aleatório, leve esse conhecimento para o código. Quando a máquina finalmente passa a conhecer as limitações de um valor, ela consegue te ajudar. E o enum é um dos mecanismos mais utilizados para modelar tipos que terão opções limitadas de valores.
Em algumas linguagens, um enum não passa de um pequeno detalhe, mas no Swift, os enums são incrivelmente poderosos e flexíveis. Eles nos permitem trabalhar de uma maneira muito segura e organizada. No exemplo desse vídeo, nós vamos modelar o enum capaz de armazenar as cores de faixa disponíveis no jiu-jitsu. E iniciamos com a palavra enum. Logo em seguida, nós definimos um nome para o enum. É interessante que você inicie com letra maiúscula. E dentro do enum, nós definimos todas as possibilidades que um valor desse novo tipo pode ter. Nós utilizamos a palavra case para definir cada uma dessas possibilidades.
O enum vai ser um tipo completamente independente. Ele vai funcionar da mesma maneira que nós fazemos com as classes e as structs.
A partir de agora, nós conseguimos criar variáveis que irão armazenar valores desse tipo. E através do ponto, nós conseguimos visualizar todas as possibilidades e também gerar um valor daquele tipo. Essa variável permite guardar informações que representam a cor de faixa de alguém no jiu-jitsu, e o valor que estamos colocando lá dentro é a faixa preta. Se você tentar armazenar um valor que não foi disponibilizado nos cases do enum, você vai ter um erro.
E diferente da maioria das linguagens de programação, no Swift, o enumeration não tem o valor inteiro atribuído a ele. Você vai notar que a variável consegue guardar informações apenas desse novo tipo. Só vai ser possível armazenar valores que foram disponibilizados nos cases do enum. E o legal é que o Swift é type safe. Como as possibilidades foram completamente limitadas, a máquina já conhece qual tipo de informação aquela variável vai suportar. E por conta disso, vai ser possível utilizar um atalho. Você vai gerar um valor diretamente através do ponto. Existe também mais uma situação e pode te ajudar a reduzir o código.O enumeration vai continuar com cases completamente independentes. A única diferença é que agora criamos todas as opções em uma única linha. Não está errado fazer uma comparação de string, mas em alguns momentos isso não é recomendado porque pode ser muito perigoso. Quanto mais possibilidades houverem um valor, maiores serão os riscos de uma informação indesejada afetar o seu projeto. Existem muitas validações que eu posso criar aqui para me proteger contra as possibilidades de uma string. Mas quando você limita as opções de um valor, a máquina vai poder te ajudar no processo de validação.
Em uma única linha, nós estamos garantindo que o valor enviado não vai ser um valor em branco ou um valor com erro de ortografia. Como vocês viram, os enumeradores conseguem de uma maneira muito fácil modelar tipos que terão opções limitadas de valores. E por conta disso, ele trabalha muito bem em parceria com o switch case. Uma dica, quando você estiver trabalhando com o switch case que vai fazer a leitura de um enumerador, você pode criar o switch em branco. Como a máquina já conhece todos os possíveis valores de um enumerador, o Xcode vai sugerir a geração automática de todos os cases pendentes.
E para cada possibilidade de valor disponível no enumerador, foi gerada uma possibilidade de valor disponível no switch. A partir de agora, o switch case vai funcionar normalmente como se estivesse fazendo a leitura de qualquer outro valor. Você vai conseguir utilizar o compound case para agrupar cenários em único case. E assim como em qualquer outra situação, se você não quiser cobrir todas as possibilidades do valor dentro do switch case, você pode fazer uso da opção default.
Existe também a possibilidade de definir um valor fixo para cada case de um enum. Na frente do nome do enumerador, nós inserimos os dois pontos e o tipo inteiro. Quando fazemos isso, nós estamos habilitando esse enumerador a trabalhar com valores brutos ou também conhecido como Raw Values. Mesmo depois de ter habilitado valores brutos do tipo inteiro, o resultado no console continua o mesmo. No momento que habilitamos essa nova funcionalidade, o nosso enumerador ganhou uma propriedade chamada Raw Value.
Nesse caso, nós estamos utilizando a cor branca. E como essa cor é a primeira opção do enum, ela vai receber o valor bruto de zero. E logo em seguida, as demais opções automaticamente irão receber mais um como Raw Value. O processo vai ficar muito parecido com que estamos acostumados
O processo vai ficar muito parecido com o que estamos acostumados a fazer nas outras linguagens de programação. Se você quiser alterar esse comportamento, você pode modificar manualmente o valor de um enum. Alteramos o valor da primeira opção e automaticamente as demais opções continuaram a sequência. Mas se trabalhar em sequência e não for interessante para você, você pode continuar definindo manualmente o valor de cada case. Nós estamos definindo valores fixos para cada opção. Uma vez definido, um valor nunca mais pode ser modificado. Na linha 12, nós utilizamos o ponto para inicializar esse valor de uma forma rápida. Mas agora que existe valor, pode ser que em algum momento você precise inicializar uma opção através desse valor.No momento que habilitamos o valor, esse enum também ganhou um novo inicializador. O problema é que, diferente dos cases do enum, os valores não são opções limitadas de valores. Esse processo de inicialização pode dar errado. Como estamos trabalhando com o inteiro, é possível inserir um valor aqui dentro que não faça referência a nenhuma das opções do enumerador. Sempre que você inicializar um enumeration através de um valor, o resultado será um valor opcional. E você precisa garantir que o valor desejado é de fato um valor que existe. Até agora nós utilizamos valores do tipo inteiro, mas no enumeration é possível trabalhar com valores fixos de outros tipos, como por exemplo uma string. Quando estamos trabalhando com valores desse tipo e não fazemos uma definição manual desse valor, a máquina automaticamente vai gerar valores contendo uma string com o mesmo nome de cada case. No entanto, o mesmo problema que nós tínhamos trabalhando com o inteiro, nós também vamos ter trabalhando com strings. Uma das principais vantagens de um enumeration é modelar um tipo que vai possuir opções limitadas de valores, diminuindo as chances de erro. E quando habilitamos os valores, nós estamos trabalhando com tipos que não possuem opções limitadas de valores. E por isso, gerar um valor através de um valor pode ser perigoso. Você vai precisar fazer mais validações, incluindo o processo correto de tratamento de erros. Então, sempre que você estiver criando um novo enumerador, faça uma reflexão para saber se é realmente necessário habilitar os valores.
Como vocês viram, os enumeradores são completamente independentes porque definem um tipo próprio. No entanto, existem situações em que precisamos de mais informações e pode ser difícil descrever um cenário com base apenas no nome de um case. E em situações como essa, nós podemos utilizar os valores associados. Os associated values são simplesmente valores extras que cada case vai poder armazenar de acordo com a sua realidade.
Valores associados são diferentes de valores. Eles não passam de informações adicionais que não interferem no funcionamento básico do enumeration. É uma alternativa mais leve. Vocês viram que quando trabalhamos com valores, a definição dos valores é feita na hora que estamos criando o enumeration. E uma vez que esses valores foram criados, não será mais possível nenhuma alteração. Já no caso dos associated values, a definição dessas informações é feita na hora que estamos criando uma variável. Pode parecer estranho, mas nada mudou. O enumeration por si só é um tipo completamente independente e esse tipo possui opções limitadas de valores. Nesse exemplo, nós escolhemos a faixa preta. Vai ser possível enviar informações adicionais, valores associados. E nessa abordagem, os valores associados poderão sofrer alterações sem nenhum tipo de restrição. E imagine os associated values como os parâmetros de uma função. Sempre que utilizamos uma função, podemos enviar informações adicionais através dos parâmetros. A configuração desses valores é feita nos cases. Cada um deles terá autonomia para solicitar a informação que achar necessário. Criamos uma variável que vai conter o conteúdo da faixa preta com algumas informações adicionais. Depois, criamos um switch case que vai analisar o conteúdo dessa variável.
Tanto na faixa marrom, como na faixa preta, nós realizamos um binding. Coletamos os valores associados e os inserimos dentro de constantes que estão identificando esses valores. Se por acaso o valor enviado for da faixa marrom, esse valor adicional vai representar o período que esse aluno está na faixa marrom. Para realizar esse binding, você não precisa decorar o tipo de informações que cada case possui. O Xcode automaticamente vai te passar essa informação. Se por acaso o valor enviado for da faixa preta, coletamos a primeira informação da dupla, que vai representar o nível dentro da faixa, e também coletamos a segunda informação da dupla, que vai determinar quanto tempo esse aluno está nessa faixa. Na criação de um enumeration, você vai precisar decidir qual das duas abordagens faz mais sentido para o seu código, valores ou valores associados. Não será possível criar um enumeration que tire proveito dessas duas abordagens. A minha recomendação para vocês é a seguinte: inicie dando preferência aos valores associados. E se durante o projeto você encontrar alguma dificuldade, faça a alteração desse enumeration para valores. Existem momentos onde pode ser interessante armazenar todos os cases de enumeration dentro de um array.
Para habilitar algumas funcionalidades necessárias, fizemos com que o enumeration seguisse um protocolo. Visualmente o processo fica muito parecido com os valores. A diferença é que ao invés de inserir um tipo de valor, nós estamos inserindo um protocolo. Após inserir este protocolo, o enumeration liberou uma nova propriedade chamada All-Cases. Em apenas uma única linha criamos um novo array contendo todos os cases disponíveis na enumeration. Esse array foi gerado contendo seis informações. São todas as opções disponíveis no enumeration. Isso significa que esse array é uma coleção que vai conter valores do tipo que foi moldado pelo enumeration. E assim como em qualquer outro array, é possível criar um loop para automatizar a leitura desses dados. Assim como struct, o enumeration é um value type. Armazena diretamente a informação e consegue manipular esse valor sem nenhuma interferência. E as similaridades não param por aí.
O enumeration também possui funcionalidades que normalmente vemos na classe ou na struct. Como por exemplo, propriedades, inicializadores e métodos. Isso pode parecer estranho para as pessoas que já programam em outras linguagens. Mas à medida que vamos nos aprofundando nesse assunto é possível perceber que no Swift os enumerations são muito poderosos. Com relação às propriedades, é possível criar valores computados ou até mesmo propriedades de tipo para providenciar mais informação para o enumeration. Através dos inicializadores, nós podemos customizar o processo de criação de valores desse tipo. Criamos um inicializador que vai determinar a cor da faixa de um aluno. E faremos isso através da quantidade de tempo que essa pessoa pratica jiu-jitsu. Se esse aluno tiver menos de um ano de experiência, o valor do enumeration será a faixa branca. Mas se esse aluno tiver mais de um ano de experiência, ele vai iniciar na faixa azul. Através dos métodos, nós podemos criar funcionalidades específicas por enumeration.
Como por exemplo, funcionalidades que vão validar o valor desse enum. Criamos um método que vai alertar caso o aluno esteja utilizando a faixa vermelha. Apesar de fazer os métodos, os enumerations também suportam chamadas recursivas onde, através dos valores associados, nós enviamos outros enumerations. Nós também conseguimos utilizar as extensões para estender as funcionalidades originais de um enumeration. No entanto, por mais que o enumeration tenha diversas funcionalidades poderosas, o seu maior diferencial continua sendo a facilidade de modelar tipos com opções limitadas de valores. Nesse vídeo, nós exploramos os principais conceitos e características de um enumeration. Modelamos novos tipos e interagimos com esses valores através do switch case. Nesse vídeo, nós também exploramos os valores associados, entre outras funcionalidades que tornam esse mecanismo algo muito poderoso nessa linguagem.
Eu espero que vocês tenham gostado. A gente se encontra no próximo vídeo. Um grande abraço a todos. Estamos juntos!