<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Davy Sousa</title>
    <description>The latest articles on DEV Community by Davy Sousa (@davy_sousa_6b798d6118e7bc).</description>
    <link>https://dev.to/davy_sousa_6b798d6118e7bc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3176706%2Fe58b162b-b313-42b4-8f32-4445cca1020e.JPG</url>
      <title>DEV Community: Davy Sousa</title>
      <link>https://dev.to/davy_sousa_6b798d6118e7bc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davy_sousa_6b798d6118e7bc"/>
    <language>en</language>
    <item>
      <title>MVVM sem mistério: projeto prático com ViewCode no iOS</title>
      <dc:creator>Davy Sousa</dc:creator>
      <pubDate>Mon, 18 Aug 2025 21:56:26 +0000</pubDate>
      <link>https://dev.to/davy_sousa_6b798d6118e7bc/aprendendo-mvvm-na-pratica-1m6f</link>
      <guid>https://dev.to/davy_sousa_6b798d6118e7bc/aprendendo-mvvm-na-pratica-1m6f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introdução&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ultimamente, tenho visto muita gente em fóruns e comunidades iOS com dificuldade pra entender MVVM na prática. Posso dizer por experiência própria que isso é bem comum, pois tive uma falha gigante nesse quesito também. Mas, com ajuda desse artigo, vamos esclarecer como funciona essa arquitetura através de um projeto de descobrir o signo de acordo com a data de aniversário.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desenvolvimento&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sobre o projeto:&lt;/em&gt; Vamos desenvolver um projeto simples que consiga explicar bem as regras e boas práticas do MVVM. Um app onde o usuário digita a data de nascimento e, ao clicar no botão, descobre o signo.&lt;/p&gt;

&lt;p&gt;Sobre a arquitetura: Na prática, MVVM consiste apenas em dividir responsabilidades do projeto para cada arquivo, basicamente uma versão "bombada" do MVC. &lt;/p&gt;

&lt;p&gt;Se divide em:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Model&lt;/em&gt;: camada responsável por conter a estrutura de dados da aplicação, como por exemplo strucks, enums e etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;View&lt;/em&gt;: camada responsável apenas pela interface que o usuário ver na tela do seu iPhone, não contendo regras de negócios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;ViewModel&lt;/em&gt;: camada responsável por toda lógica de negócio da aplicação, não acessando a view, e utilizando os dados presentes na Model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6euebptc5tefm4c6ugt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6euebptc5tefm4c6ugt.png" alt=" " width="771" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta imagem representa o fluxo de uma MVVM padrão:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;View mostra ao usuário um botão, por exemplo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A controller acessa esse botão, e chama a viewModel &amp;gt; "Ei, o usuário clicou no botão, o que eu faço?"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A viewModel faz toda manipulação com as regras de negócio, usando a Model, e devolve para a controller que irá mostrar ao usuário o resultado final da sua ação.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Agora vamos para a prática. E por questão de acelerar o processo, vou mostrar os screenshots das telas e explicar a funcionalidade através de comentários no código e falar sobre como foi aplicado a teoria do MVVM.&lt;/p&gt;

&lt;p&gt;Antes de tudo, vale dizer que usei ViewCode em vez do AutoLayout do Storyboard, pois sinto que tenho mais controle para montar telas e dividir responsabilidades. As configurações para implantar o ViewCode não estarão disponíveis nesse artigo, porém é de fácil acesso em outros materiais e fóruns.&lt;/p&gt;




&lt;p&gt;Vamos começar com a divisão de pastas e criação de arquivos. Como é um projeto mais simples, sei exatamente quais arquivos vamos precisar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbsrue3f72w83ct4j408.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbsrue3f72w83ct4j408.png" alt=" " width="236" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antes de começar a codar, criamos uma pasta para cada camada: Contoller, View, ViewModel e Model. Outras pastas como Suport e Resource é mais para organização do projeto.&lt;/p&gt;

&lt;p&gt;Agora vamos definir o fluxo da nossa aplicação:&lt;/p&gt;

&lt;p&gt;O usuário coloca sua data no campo de texto e ao clicar no botão, mostra uma label com o resultado. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeiyje1lzfbko2lnm4i6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeiyje1lzfbko2lnm4i6.png" alt=" " width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interface com botões e textos&lt;/em&gt;: View&lt;br&gt;
&lt;em&gt;Lógica de quando o user clicar no botão&lt;/em&gt;: ViewModel&lt;br&gt;
&lt;em&gt;Enum com cada caso de uso para cada mês&lt;/em&gt;: Model&lt;br&gt;
&lt;em&gt;Atualizar a view com o resultado da ViewModel&lt;/em&gt;: Controller&lt;/p&gt;




&lt;p&gt;View: Interface do projeto&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1le2gb6fo31faa349x2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1le2gb6fo31faa349x2r.png" alt=" " width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eu sei que talvez não tenha caprichado tanto no layout, mas ele mostra bem o que precisamos, então está ótimo. haha&lt;/p&gt;

&lt;p&gt;Na View temos um título, uma label e um textfield dentro de uma Stack View, uma label que só aparece com o resultado, e por fim, um botão.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5zoqqq2ub0usm1yocgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5zoqqq2ub0usm1yocgk.png" alt=" " width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frm7fgh7kec4qx5rkeaqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frm7fgh7kec4qx5rkeaqt.png" alt=" " width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2nol5o9yx0oqdlrq81q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2nol5o9yx0oqdlrq81q.png" alt=" " width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmfk0ct17vsl553pjcq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmfk0ct17vsl553pjcq0.png" alt=" " width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O código está comentado para te ajudar a entender o que cada parte é responsável. Mas o que é mais importante: Na View apenas tratamos da interface do projeto, botões, labels e etc. Nada de regras de negócio por aqui.&lt;/p&gt;

&lt;p&gt;Uma preferência minha é utilizar delegante para fazer a comunicação do botão com a controller, porém tem outros métodos para fazer isso.&lt;/p&gt;




&lt;p&gt;Controller: Ponte entre View e ViewModel &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07b3nztwwttujh3hw961.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07b3nztwwttujh3hw961.png" alt=" " width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A controller realmente deve ser apenas uma ponte, nada de regras de negócios ou UI da aplicação. Pega o que a view oferece, recebe o resultado da viewModel e mostra ao usuário.&lt;/p&gt;




&lt;p&gt;ViewModel: Regra de negócio&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp64474qs2y90hs3xeem5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp64474qs2y90hs3xeem5.png" alt=" " width="755" height="854"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A viewModel acaba sendo onde o código vai ser mais trabalhado, pois vamos lidar com toda a lógica. Mas tem uma regra: Não podemos acessar a view, pois deve lidar apenas com as regras. Tenho um macete para isso, se o código pedir import de UIKit, você está fugindo da responsabilidade da ViewModel.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var onSignText: ((String) -&amp;gt; Void)?&lt;br&gt;
 var onErrorText: ((String) -&amp;gt; Void)?&lt;br&gt;
 var onButtonEnabled: ((Bool) -&amp;gt; Void)?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Atenção para essas linhas, pois serve de comunicador com a controller, como usamos aqui: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;func bindViewModel() { &lt;br&gt;
        viewModel.onSignText = { [weak self] text in&lt;br&gt;
            self?.screen.resultLabel.text = text&lt;br&gt;
            self?.screen.resultLabel.textColor = .brown&lt;br&gt;
        }&lt;br&gt;
        viewModel.onErrorText = { [weak self] text in&lt;br&gt;
            self?.screen.resultLabel.text = text&lt;br&gt;
            self?.screen.resultLabel.textColor = .systemRed&lt;br&gt;
        }&lt;br&gt;
    }&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;Model: Estrutura de dados&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firwvow8w3p7ktcxm499u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firwvow8w3p7ktcxm499u.png" alt=" " width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Model é onde fica toda estrutura de dados do projeto, e nesse caso, todos os cases de uso para encontrar o signo baseado na data de aniversário. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusão&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O MVVM é basicamente isso, cada camada com sua responsabilidade. Lógico que em aplicativo menores você pode optar por usar outra arquitetura, como MVC, e em projetos maiores, algo como VIPER.&lt;/p&gt;

&lt;p&gt;Não é difícil a compreensão desses conceitos, desde que você entenda a lógica de divisão de responsabilidade, e a partir daí, é só replicar para cada tela da aplicação.&lt;/p&gt;

&lt;p&gt;Acredito que esse artigo pode te ajudar a entender melhor essa arquitetura, pois eu também sofri com esse obstáculo, e acabava por sempre optar por arquiteturas mais fáceis.&lt;/p&gt;




&lt;p&gt;Curtiu? Se tiver dúvidas ou quiser trocar ideia, é só me chamar no Instagram - &lt;a href="https://www.instagram.com/davy.developer/" rel="noopener noreferrer"&gt;https://www.instagram.com/davy.developer/&lt;/a&gt; — tamo junto nessa jornada iOS! 🚀&lt;/p&gt;

</description>
      <category>programming</category>
      <category>swift</category>
      <category>mobile</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
