Como funciona o padrão de arquitetura do MVVM no Xcode

  • 00:00 - Introdução

    E aí meu povo, tudo bom com vocês? Eu sou Michel Lopes e ajudo pessoas a aprender a linguagem do Swift de uma forma simples. Nesse vídeo nós vamos explorar dois assuntos que são cruciais no SwiftUI. O primeiro deles será a introdução ao padrão arquitetural do MVVM e o segundo assunto será a propagação e compartilhamento de dados em SwiftUI.

    Se no desenvolvimento de um código você já sofreu por conta da falta de organização, dificuldade no trabalho em equipe ou até mesmo foi perdendo a motivação, à medida que o código foi crescendo, esse conteúdo pode te ajudar, porque na maioria das vezes esses problemas são simples consequências de uma falha na arquitetura do seu projeto.

    Com o passar dos anos, notou-se que a maioria dos programadores sempre sofreu exatamente com os mesmos problemas quando um novo projeto se iniciava e, por conta disso, começaram a surgir os padrões de desenvolvimento. Esses padrões, nada mais são do que manobras que ficaram famosas por ajudar a prevenir problemas comuns que acontecem durante a criação de um projeto.

    Existem os padrões de projeto. Bastante conhecidos como design patterns, esses padrões nos auxiliam com problemas recorrentes causados pelo código de um projeto. E também existem os padrões de arquitetura. Já esses padrões nos ajudam a solucionar problemas causados especificamente pela estrutura de um projeto.

    O MVVM é um padrão de arquitetura e é um dos mais utilizados entre os desenvolvedores iOS. Isso se deve ao fato de que esse padrão consegue se adequar muito bem às principais características da plataforma. E quando o comparamos com outras alternativas, concluímos que o esforço de implantação dessa arquitetura é relativamente baixo.

    O MVVM traz muitos benefícios e as principais vantagens são: ele te ajuda a gerar um código muito mais organizado e legível, facilita o reaproveitamento de código, simplifica a manutenção e a atualização de um projeto, facilita o trabalho em equipe e também simplifica a criação de testes no seu projeto.

    Mas nem tudo são flores e o MVVM também possui algumas desvantagens. Um dos maiores problemas dessa arquitetura é a sensibilidade na comunicação de dados entre as suas camadas. Não se preocupe se você ainda não está familiarizado com esses termos. O importante aqui é que você entenda que, por conta dessa sensibilidade na comunicação de dados, projetos criados em tecnologias como UIKit precisam de adaptações extras para conseguir implantar essa arquitetura com perfeição. E é nesse momento que o SwiftUI entra em cena. Projetos criados com essa tecnologia conseguem ser muito bem atendidos pelo padrão arquitetural do MVVM.

    01:06 - Arquitetura MVVM

    O padrão de arquitetura do MVVM consiste basicamente em dividir o seu projeto em três camadas: a model, a view e a view model. Esses nomes podem soar um pouco estranhos para você, mas não se engane. Isso é mais fácil do que parece. E para provar isso, iremos explorar

    essa arquitetura fazendo uma simples analogia com um time de futebol infantil.

    Paulo foi contratado para treinar um time de futebol de uma escola. No seu primeiro dia de trabalho, Paulo chega na escola e se depara com 23 meninos correndo loucamente e se debatendo por uma única bola no centro da quadra. Então, ele chega à conclusão de que o seu primeiro desafio como treinador será o de separar os jogadores em posições, definindo a responsabilidade de cada um em campo.

    Assim como Paulo, o principal objetivo do MVVM é organizar o seu projeto em camadas, fazendo uma simples separação de responsabilidades. Seguindo com a nossa analogia do time de futebol, podemos dizer que no MVVM, a posição de zagueiro será exercida pela camada model. A camada model tem a responsabilidade de proteger e manusear o que temos de mais precioso dentro de um aplicativo, que são os dados. Essa camada tem esse nome porque normalmente é aqui que ficam os modelos de dados do nosso projeto.

    Já do outro lado, nós temos a camada View. Ela é conhecida por fazer a linha de frente de uma aplicação e, por isso, seguindo a nossa analogia, ela irá exercer a função de atacante. A View é a única camada que será visível ao usuário final. Ela tem a responsabilidade de definir e exibir a interface do aplicativo.

    Então, em um dos lados, nós temos a camada model, que, como um bom zagueiro, vai se preocupar em proteger e manusear os dados da aplicação. E na outra ponta, nós temos a camada View, que assume toda a responsabilidade de aparência da aplicação e vai se preocupar apenas em atacar o usuário final com uma boa interface.

    Por mais que essas duas camadas tenham responsabilidades completamente diferentes, elas fazem parte do mesmo time e precisam, de alguma maneira, trabalhar em equipe. Caso contrário, a View não vai conseguir exibir os dados da model na interface e, consequentemente, as interações do usuário não irão afetar os dados do app.

    Para coordenar a comunicação e garantir que existe uma colaboração entre essas duas camadas, nós temos a View Model atuando no meio de campo. Por mais que no nosso time, o atacante tenha um papel fundamental, ele não consegue fazer muita coisa além de simplesmente chutar a bola para o gol. E por conta disso, o nosso meio de campo tem um papel muito importante no time, que vai buscar a bola nas águas, armar a jogada e colocar o atacante literalmente na cara do gol.

    A View Model tem a responsabilidade de processar e disponibilizar para a View os dados que serão exibidos na interface. A maneira mais fácil de entender o que a View Model realmente faz é imaginar ela como sendo o cérebro da camada View.

    Tudo o que vimos até agora foi a teoria e a burocracia por trás desse padrão arquitetural chamado MVVM, mas não se preocupe. Tudo isso vai fazer mais sentido e ficar mais divertido quando a gente conseguir ir para a parte prática desse vídeo.

    08:12 - MVVM na prática

    Seja muito bem-vindo à parte prática desse vídeo. A grande maioria dos vídeos da internet que falam sobre MVVM abordam esse assunto através da criação de um aplicativo que possui uma lista dinâmica de dados, e não tem nada de errado com isso. Mas dependendo dessa lista, podemos acabar trazendo uma complexidade desnecessária para a mesa. E além disso, eu percebo que algumas pessoas que aprendem MVVM dessa forma pensam que essa arquitetura só é aplicada em situações similares a de uma lista dinâmica.

    Nós iremos explorar esse assunto através da criação de uma interface muito simples. Você vai conseguir visualizar a separação de responsabilidades entre as camadas do MVVM e depois, será capaz de aplicar esse conhecimento em diferentes situações. O projeto que nós vamos construir se chama Follow e é mais ou menos uma rede social. Quando o usuário decidir seguir um perfil, tocando no botão de Follow lá embaixo, o número de seguidores do perfil vai aumentar e, consequentemente, alguns eventos vão acontecer nessa interface.

    Talvez você esteja achando essa interface algo muito complexo de se criar, mas eu vou te provar que é bem mais fácil do que parece. Nós iremos começar essa interface com uma imagem e três textos. Temos a foto, o nome do perfil, o nickname e a quantidade de seguidores. Agora vamos colocar aqueles dois botões lá embaixo. Terminamos a nossa interface.

    Na parte de cima desse stack, nós vamos ter a imagem e três textos. E lá na parte de baixo da interface, nós temos dois botões dentro de outro stack. Essa interface foi construída, mas todos os dados são fictícios. Então nosso primeiro passo vai ser possibilitar a entrada de dados reais nessa interface. Precisamos armazenar um valor que vai representar o nome da imagem, o nome do perfil, o nick do perfil e também a quantidade de seguidores.

    O jeito mais comum de possibilitar a entrada de dados reais é através da criação de variáveis e constantes. Com base nos dados que a nossa interface precisa, nós criamos quatro variáveis, elas vão servir como uma espécie de repositório. Mas ao invés de deixar essas variáveis soltas aqui em cima, faz muito mais sentido modelar um novo tipo, o tipo usuário. E todas aquelas variáveis vão ser simplesmente propriedades desse novo tipo.

    Lá na nossa view, nós criamos uma propriedade chamada usuário, e ela vai ser do tipo de dado que foi modelado lá em cima. Ela vai ser uma espécie de repositório, onde nós vamos armazenar valores verídicos posteriormente. Então o próximo passo é fazer com que a interface para exiba valores fictícios e comece a exibir valores desse repositório. Por mais que visualmente a nossa interface continue quase a mesma coisa, agora ela é capaz de receber valores verídicos através dessa propriedade chamada usuário.

    Essa alternativa é a maneira mais utilizada entre as pessoas que estão começando a trabalhar com SwiftUI, mas pode ser muito perigoso trabalhar dessa forma. Fazendo com que a interface acesse diretamente a informação diretamente o dado. Existem grandes chances de lá na frente você encontrar alguns problemas já conhecidos entre os desenvolvedores. E é justamente para evitar esses tipos de problemas que existem os padrões arquiteturais, no nosso caso o MVVM. Lembra o MVVM é basicamente a separação do seu projeto entre as camadas, a model, a view e a view model.

    A camada que faz a linha de frente do nosso time é a camada view, ela é responsável por toda a aparência da nossa aplicação. Portanto, todo código que está criando uma interface para o usuário a gente caracteriza como camada view. No nosso exemplo essa camada é basicamente toda aquela view chamada de Profile View. E do outro lado nós temos a camada model. Olha como esses nomes fazem muito mais sentido agora. Não foi com base na nossa interface que nós geramos alguns dados e depois criamos um modelo de dados para que a gente pudesse transportar essas informações para interface. Então, todos os modelos de dados da sua aplicação ficam na camada model. Nós temos o modelo de dados chamado User para proteger e manipular as informações dos usuários na nossa aplicação.

    De acordo com MVVM, não é legal fazer com que a sua camada view acesse diretamente a camada model. Para isso nós temos a view model atuando no meio de campo. Lembra que eu falei para vocês que a camada view não sabe fazer muita coisa ou pelo menos não deveria saber fazer muita coisa? Ela simplesmente chuta bola para o gol. Todo o trabalho de processamento de dados e coleta de dados é feito pela camada view model. O nosso próximo passo será criar uma view model.

    Criamos uma struct chamada Profile ViewModel. Como a view model é considerada, o cérebro da view é uma boa prática criar essa camada utilizando o mesmo nome da view, mas view model no final. A partir de agora todo o trabalho sujo que acontece na camada view, qualquer processamento ou código que não esteja literalmente relacionada com a construção de uma interface precisa ser movido para a camada view model. Movemos o acesso ao dado para a camada view model. E lá na camada view nós criamos uma propriedade chamada ViewModel, e ela vai ser do tipo Profile ViewModel.

    A partir de agora a propriedade User não faz mais parte da camada view, ela será providenciada pela camada viewModel. Esse código ainda pode evoluir bastante, mas a lógica básica por trás do MVVM é essa, uma definição clara entre as responsabilidades que cada parte do código vai ter no seu projeto. Parece que a gente está simplesmente jogando partes do código de um lado para o outro, mas é exatamente a separação dessas três camadas que torna o MVVM e um padrão arquitetural tão poderoso e que nos ajuda a evitar vários problemas no desenvolvimento de um projeto.

    Parece que a gente está simplesmente jogando partes do código de um lado para o outro, mas é exatamente a separação dessas três camadas que torna o MVVM um padrão arquitetural tão poderoso e que nos ajuda a evitar vários problemas no desenvolvimento de um projeto. Uma das maiores dificuldades do MVVM é a transição e sincronização de dados entre essas três camadas. Repare que para conseguir exibir uma informação correta na interface, a camada View depende completamente da camada ViewModel. Isso significa que sempre que a camada ViewModel fizer uma alteração em um valor, imediatamente a camada View precisa se atualizar para exibir a informação correta. Imagine que esteja acontecendo aqui na ViewModel uma consulta em um banco de dados, e aquele valor que foi consultado é um valor diferente do que está sendo exibido na interface. Se a camada View não se atualizar buscando novamente aquele valor, ela estará exibindo uma informação incorreta.

    Mesmo que a sincronização da informação entre as camadas seja o ponto fraco do MVVM, o SwiftUI não tem nenhum problema para implementar esse padrão arquitetural. Porque praticamente toda a framework do SwiftUI foi construída em cima desse conceito de proteção da informação, evitando a inconsistência de dados.

    E para conectar essas duas camadas, fazendo com que a View sempre reaja a alterações feitas na camada ViewModel, nós podemos utilizar o State. Esse State é um próprio wrapper, eu já fiz um vídeo em detalhes só falando desse carinha aqui, ele é fundamental no SwiftUI. É muito importante que você entenda exatamente o que está acontecendo aqui. Vou deixar o link na descrição desse conteúdo. Mas de qualquer forma, vou resumir bem rapidinho só para relembrar.

    Quando inserimos o State na frente de uma propriedade, nós estamos criando uma "Source of Truth", ou seja, uma fonte da verdade. A partir do momento que o SwiftUI detectar uma modificação nesse valor, ele fará com que o body da sua View seja invalidado, forçando-a a reconstruir a interface. Então, quando utilizamos esse próprio wrapper do State e criamos uma "Source of Truth", a nossa View vai dar todo o crédito àquele valor. A partir do momento que ela detectar qualquer modificação nessa "Source of Truth", ela vai se reconstruir automaticamente.

    E lembra, o SwiftUI é super esperto em relação à performance. Ele funciona no estilo declarativo. Ele é capaz até mesmo de detectar se será necessária uma reconstrução completa da View ou apenas uma reconstrução em partes específicas da interface.

    Para que vocês possam ver isso em ação, eu vou criar uma função lá dentro da ViewModel, e ela vai atualizar o Nick do nosso usuário. Lá no final da interface, nós iremos chamar o evento onAppear(). Sempre que a nossa tela aparecer, nós iremos chamar o método updateUser() lá da ViewModel. 

No exato momento em que a sua view apareceu na tela, a sua view model atualizou o Nick dos usuários. E por conta disso, a view foi reconstruída exibindo o novo Nick na interface.

    Eu vou apagar esse código. Antes de prosseguirmos, na construção dessa interface, criando eventos dos botões e também fazendo o tratamento de dados relacionados ao número de seguidores, nós vamos fazer uma modificação que está incomodando um pouco. Acho que alguns de vocês já perceberam.

    Tanto a view model e a view estão expressas em um único arquivo. Com certeza, isso não é uma prática recomendada. Eu fiz dessa maneira para facilitar o entendimento de vocês, mas o ideal é que você faça uma separação clara entre cada uma dessas camadas.

    O arquivo atual se chama ProfileView, ou seja, toda a nossa camada de view está no lugar certo. Nossa próxima missão é criar dois novos arquivos, um para a model e outro para a view model. Lembre-se de que a camada view é a única camada visível aos olhos do usuário, portanto as camadas de model e view model não serão arquivos em SwiftUI e sim arquivos simples de Swift.

    Criamos a ProfileViewModel. Vamos remover o conteúdo da view model desse arquivo de view e levá-lo para o seu arquivo exclusivo. E também vamos criar o arquivo da model. O nome desse arquivo será exatamente o tipo de dado que ele está representando dentro da aplicação.

    Agora as coisas começaram a ficar um pouquinho mais organizadas nesse projeto. Quando você estiver definindo novos tipos, tente criar arquivos exclusivos para esses tipos. Isso vai facilitar bastante a sua vida lá na frente.

    17:44 - Data-flow e MVVM

    O próximo passo será fazer com que, ao clicar no botão de "follow", o botão de "enviar mensagem" seja habilitado. Criamos a propriedade "isFollowing" para determinar se estamos seguindo ou não esse perfil. E o botão de "enviar mensagem" estará habilitado apenas se estivermos de fato seguindo esse perfil.

    Essa propriedade booleana vai ocasionar na reconstrução da nossa interface. Se ela estiver verdadeira, o nosso botão de "enviar mensagem" vai estar de um jeito. Se ela estiver como falsa, o nosso botão de "enviar mensagem" vai estar de outro jeito. Sendo assim, essa propriedade precisa ser uma source of truth.

    E agora que a nossa interface também está monitorando esse valor, basta fazer a modificação dele quando o usuário tocar no botão de "follow". Agora, preste atenção, vou te dar uma dica muito importante que muita gente acaba tropeçando nesse problema quando está construindo uma interface em SwiftUI.

    Todas as vezes que nós utilizamos o @State na frente de uma propriedade, estamos criando uma source of truth. A nossa interface vai começar a monitorar aquele valor e ele vai definir se ela vai ser reconstruída ou não.

    E quando nós temos três, quatro, cinco, seis source of truth em uma única view, a sua responsabilidade é garantir que essa atualização sempre esteja acontecendo de uma maneira correta. Então, quanto menos source of truth você tiver em uma única view, melhor. Sua vida vai ficar muito mais fácil na hora de controlar a lógica dessa interface.

    Tome bastante cuidado quando você estiver criando uma interface e perceber que está cheio de propriedades com o @State. Você pode dividir essa view em pedaços menores utilizando a técnica de composição de views ou utilizar a sua view model através do padrão arquitetural do MVVM.

    A partir do momento em que essa propriedade isFollowing deixa de ser uma simples informação que é exibida na tela para começar a controlar a lógica de exibição por trás da interface, faz muito sentido que esse processamento seja feito dentro da nossa view model. Levante a propriedade lá para dentro da view model e o legal é que podemos remover o @State e tudo vai continuar funcionando como antes. Isso porque agora o isFollowing é uma propriedade da nossa view model.

    Lembra que lá na view a nossa view model já está marcada com o @State? Então, qualquer modificação em uma propriedade dessa struct já vai ocasionar na modificação da própria view model e, consequentemente, na atualização da interface.

    Eu vou criar um método que vai atualizar essa informação se o usuário está seguindo ou não o perfil atual. E, na nossa view, vamos fazer duas alterações: a primeira no botão de "seguir", vou executar aquele método da view model; e também atualizar aquela informação que determina se o segundo botão está habilitado ou não. Vamos executar. Tudo funcionando perfeitamente.

    A diferença é que agora nós temos uma única source of truth. Todo aquele processamento que é feito em cima daquela propriedade isFollowing está sendo feito na nossa camada intermediária, está sendo feito pela nossa view model.

    Eu não sei se você lembra, mas ainda faltam alguns ajustes com relação aos botões dessa interface.

    Finalmente chegou a hora de conseguirmos exibir corretamente a quantidade de seguidores do perfil atual. Esse número acabou perdendo um pouco a formatação. Nossa missão agora é processar esse dado e deixá-lo preparadinho para que a interface consiga exibir corretamente para o usuário.

    Quem vai fazer isso é a nossa view model, porque ela é o cérebro da camada view. O número de seguidores está sendo disponibilizado para a interface diretamente da propriedade user. Mas o que pouca gente sabe é que, com esse padrão do MVVM, nós podemos criar novas propriedades e disponibilizá-las para a view exatamente o que ela precisa.

    32:09 - Data-flow e Composing View

    Por mais que esse valor seja a representação de um número lá na nossa interface, ele é exibido como uma string. Nada mais justo do que a nossa view model carregar essa informação e preparar corretamente o que a interface vai exibir. Vou criar bem rapidinho um método que vai processar um valor numérico e retornar uma string já no formato brasileiro, que é tudo o que a nossa interface precisa.

    Nesse método, enviamos um valor do tipo Double. Ele faz todo o processamento de conversão para o formato brasileiro e depois retornamos esse número como sendo do tipo String contendo a letra "K" no final dessa string.

    Sempre que a nossa view model for inicializada, vamos atualizar a propriedade userFollowers com base na quantidade atual de seguidores que esse usuário possui.

    A nossa view model ainda pode melhorar, mas saiba que você sempre tem a opção de "traduzir" a informação original para a view e disponibilizar novas propriedades com dados processados, para que a view receba exatamente o que será exibido na interface.

    E está faltando um detalhe importante. Quando clicarmos no botão de "Seguir", o número de seguidores precisa incrementar mais um, ou decrementar quando deixarmos de seguir esse perfil.

    Por mais que o nosso projeto esteja aparentando estar na sua versão final, eu não estou muito satisfeito com a qualidade do código dessa interface. Está muito grande. A gente pode melhorar isso com aquela técnica de composição de views, quebrar grandes pedaços em pedaços menores para fazer a combinação desses componentes, o famoso "dividir para conquistar". Lembrando que eu já fiz um vídeo específico sobre esse assunto, e para te ajudar eu vou deixar o link também aí na descrição desse vídeo.

    Vamos quebrar essa interface em dois pedaços. Primeiro, nós temos a ProfileHeader View, que são os dados ali daquele perfil. E depois, nós temos a nossa view com os botões.

    Mas nós estamos enfrentando um problema: elas não estão reconhecendo a nossa view model. Você pode estar pensando que a solução para esse problema é simplesmente copiar a nossa view model também nas nossas subviews. Mas quando executamos essa interface, a nossa view consegue modificar os nossos botões, mas as alterações dos botões não afetam o número de seguidores na parte de cima. Nós estamos literalmente trabalhando com duas views diferentes. Cada uma dessas views tem a sua própria source of truth. Elas não se comunicam, é como se cada uma estivesse armazenando e modificando informações individualmente.

    Lá em cima, na nossa ProfileHeader, nós já criamos uma source of truth. Então, aqui na subview, não faz sentido criar uma nova source of truth. Nós vamos simplesmente acessar aquela que já foi criada lá em cima.

    Nós utilizamos o @Binding. Com isso, é possível propagar um valor que poderá ser manipulado por diferentes views dentro da interface. Quando você fizer isso, ao chamar a subview, você é obrigado a passar essa informação da source of truth. Criamos uma única source of truth e propagamos essa informação para os demais subviews.

    Então, resumindo, quando você quiser criar uma source of truth simples, você utiliza o @State. E, para propagar essa informação para os demais subviews, para que elas também tenham acesso à mesma source of truth, você faz isso através do @Binding.

35:59 - Data-flow / @StateObject

    O nosso projeto já melhorou muito, mas ainda dá para melhorar um pouquinho mais. Até agora nós utilizamos apenas um tipo de Source of Truth, que é o State. Nós utilizamos o State quando nós queremos criar uma Source of Truth bem simples, mas dá uma olhada lá na nossa ViewModel. Você vai perceber que ela não é uma Source of Truth simples, como um booleano, um inteiro ou uma string. Tem muita coisa acontecendo aqui dentro, e a tendência é que essa camada comece a engordar à medida que a nossa interface vai ficando um pouquinho mais complexa. Todo o trabalho sujo vai sendo feito aqui dentro. Então, a nossa ViewModel é um forte candidato a não ser uma Source of Truth simples, a ser uma Source of Truth um pouquinho mais complexa.

    E para criar uma Source of Truth um pouquinho mais parruda e complexa, nós utilizamos o StateObject. Diferente do State, o StateObject exige que esse valor seja reference type, ou seja, seja uma classe e não uma struct. Além disso, o StateObject exige que essa classe siga um protocolo chamado ObservableObject.

    É muito simples. Quando você estiver armazenando valores que são Value Type, você vai utilizar o State. Quando você estiver armazenando valores que são reference type, ou seja, classes, nós vamos utilizar o StateObject. E sempre é necessário fazer com que essa classe siga o protocolo ObservableObject.

    Lembra que, no caso da struct, qualquer alteração que fazíamos em uma propriedade da ViewModel ocasionava na modificação da ViewModel inteira. Isso não acontece mais com as classes. Precisamos literalmente dizer para o SwiftUI quais propriedades irão ocasionar na atualização da interface. E fazemos isso através do Wrapper "Published". Essas são as duas propriedades que serão monitoradas pelo SwiftUI, e qualquer alteração nelas ocasionará na reconstrução da interface. Diferente do State, nós não podemos propagar essa informação utilizando Binding. Para propagar um StateObject, nós podemos utilizar o EnvironmentObject. E como não iremos propagar esse valor através de um Binding, não precisamos inserir o cifrão.

    Vamos executar. Tudo funcionando. Então, lembre-se, para criar uma Source of Truth bem simples, nós utilizamos o State, principalmente porque estamos trabalhando com Value Types, ou seja, structs, enums e por aí vai. E para criar uma Source of Truth com base em uma classe, ou seja, Reference Type, nós utilizamos o StateObject. Para propagar um StateObject, nós utilizamos o EnvironmentObject.

    40:08 - Data-flow / @EnvironmentObject

    Tome bastante cuidado para não se confundir e inicializar essa Source of Truth diretamente através de um EnvironmentObject. Isso pode ocasionar em alguns problemas dentro do seu projeto. Você sempre inicializa o valor com StateObject e a propagação desses valores com EnvironmentObject.

    E eu deixei a cerejinha do bolo para o final. Existe uma maneira muito prática de propagar a ViewModel por todas as subviews da sua interface, que é fazer essa propagação através do ambiente. Nós vamos inicializar a Source of Truth através de um StateObject, injetar esse valor no ambiente e a partir daí, qualquer subview dessa interface vai poder acessar diretamente essa informação.

    Assim como antes, nós inicializamos a Source of Truth utilizando o StateObject. E ao invés de propagar esse valor com EnvironmentObject, nós vamos utilizar o @EnvironmentObject. Com o @EnvironmentObject, nós não precisamos ficar passando o valor desejado toda vez que essa ViewModel for exibida. Nós iremos injetar a nossa ViewModel nesse ambiente através do modificador @EnvironmentObject.

    Então, olha só, criamos a nossa ViewModel com o StateObject. Injetamos essa ViewModel em todo o ambiente e a partir daí, as subviews que compõem essa interface terão livre acesso a essa Source of Truth.

    43:55 - Dicas

    Agora que você já viu tudo isso, eu vou te passar algumas dicas que podem te ajudar quando você estiver implementando esse padrão do MVVM em um projeto em SwiftUI.

    A primeira dica é: se, por algum motivo, você não fez o planejamento prévio de como vai ser a estrutura do seu projeto, não inicie implementando o MVVM logo de cara, isso pode jogar a sua produtividade lá embaixo. Nessa situação específica, eu recomendo você fazer igual a gente fez nesse projeto, criando uma interface pontuada completamente fictícia, aos poucos validando essas informações e só depois fazendo a definição de responsabilidades. Lembrando que o ideal mesmo é que você faça um planejamento prévio da estrutura do seu projeto.

    A segunda dica também é sobre produtividade e pode parecer um pouquinho boba, mas ela faz a diferença. No início, quando você está implementando as camadas do MVVM, pode ser interessante mantê-las no mesmo arquivo. Você vai transitar muito entre as camadas, fazendo as configurações necessárias. Lógico que depois você vai dividir esse projeto em arquivos completamente independentes.

    A terceira dica é com relação às pastas e arquivos do seu projeto. Normalmente, quando as pessoas estão aprendendo a implementar o MVVM, é bem comum que lá nas pastinhas do projeto, elas criem o grupo da model, da view e da viewmodel. No primeiro momento, isso pode te ajudar a identificar exatamente quais arquivos fazem parte de quais camadas, mas a real é que esse modelo depois de um tempo ele não fica muito prático. À medida que o projeto vai crescendo, fica inviável essa forma de organizar os seus arquivos. Então, eu vou compartilhar com vocês uma estrutura de arquivos e pastas muito simples que normalmente é utilizada em projetos de pequeno a médio porte.

    A pasta "Views" é dividida em duas pastas. O primeiro grupo é o grupo da Shared, que são basicamente todas as views compartilhadas no nosso projeto. Um pedacinho de view, um botão customizado, tudo que é compartilhado você pode colocar nessa pasta Shared. E temos também a pasta Screens, que são as telas da sua aplicação. Aqui dentro, para cada tela, cria-se uma nova pasta. Nesse exemplo, nós temos a Home e lá dentro nós vamos ter a HomeView e a HomeViewModel. Em seguida, a tela de configurações e lá dentro a View e a ViewModel. Depois, nós temos a pastinha Model e, por último, uma pastinha Resources, onde você pode colocar todos os arquivos que vão complementar o seu projeto.

    Não existe uma única forma de implementar o MVVM, existem diferentes abordagens e tudo vai depender do seu time e da maneira que você quer trabalhar. O interessante é que você entenda os pilares desse padrão e respeite-os durante o desenvolvimento do seu projeto.

    A quarta dica: nesse exemplo, nós utilizamos as principais maneiras de sincronizar dados no SwiftUI, mas se você quiser se aprofundar na construção de um código reativo que não necessariamente esteja envolvido com a interface, você pode se aventurar em uma framework chamada Combine na linguagem Swift.

    Nesse vídeo, nós exploramos os principais conceitos do padrão arquitetural chamado MVVM, que consiste na separação de três camadas: a Model, a View e a ViewModel. E também exploramos o fluxo e a sincronização de dados no SwiftUI.

    Eu espero que vocês tenham gostado. A gente se encontra no próximo vídeo. Um grande abraço a todos, estamos juntos!

Anterior
Anterior

SwiftUI - Guia Completo | Aprenda a criar apps nativos de iPhone no Xcode

Próximo
Próximo

Tuplas em Swift 🇧🇷 | Aprendendo com a irmandadeSwift