Alura > Cursos de Mobile > Cursos de iOS > Conteúdos de iOS > Primeiras aulas do curso Swift: aplicando o padrão arquitetural MVVM e boas práticas de separação de responsabilidades

Swift: aplicando o padrão arquitetural MVVM e boas práticas de separação de responsabilidades

Analise de projeto - Apresentação

Olá!

Eu sou o Ândriu Coelho e gostaria de dar as boas-vindas a mais um curso de iOS aqui na Alura.

Audiodescrição: Ândriu se declara como um homem branco de cabelo castanho claro curtos. Possui uma barba também curta. Está usando uma camiseta azul escuro com a logo da Alura e está sentado em uma cadeira com um encosto alto preto. Atrás dele, à esquerda, tem uma estante com diversos objetos de decoração, como lâmpadas, quadros, vasos e livros. Ao fundo, a parede está iluminada por uma luz LED roxa.

A ideia desse curso é continuarmos o desenvolvimento do app Voll Med, que já foi iniciado nos cursos anteriores, mas dessa vez com um olhar um pouco mais arquitetural. Então, entenderemos qual é o benefício de utilizarmos um padrão arquitetural em nosso projeto.

Nós vamos utilizar o MVVM (Model-View-ViewModel), com o qual conseguiremos desacoplar algumas partes do Voll. Todas as camadas de requisição, de serviços para APIs, validações, entre outros, será tratado com o MVVM. Também criaremos uma camada de networking, para separar as chamadas de APIs do nosso projeto por funcionalidade. É muito comum que, ao longo do tempo, que o projeto cresça e as pessoas coloquem tudo dentro de um só arquivo. Então, discutiremos os benefícios de conseguirmos separar isso.

A ideia do curso é realmente ter um projeto funcional, mas com um olhar sobre o aspecto de arquitetura, de modo que possamos melhorar a testabilidade do projeto, a organização do código e a reutilização do código. Como pré-requisito, é importante que você já tenha feito os cursos dessa formação para compreender o projeto.

Esse é o conteúdo que veremos durante o curso, e espero você.

Analise de projeto - Introdução à arquitetura MVVM em iOS

Para iniciar o nosso curso, eu gostaria de analisar algumas implementações feitas nos cursos anteriores. Eu já estou com o projeto Vollmed aberto e vou abrir o simulador. Quando iniciamos o simulador, nos deparamos com a tela principal, ou seja, a home do aplicativo Vollmed.

No canto superior direito está o botão de logout, no centro superior está a logo do aplicativo seguida de duas etiquetas: "Boas-vindas!" e uma mensagem informando sobre a lista de profissionais da medicina, onde a pessoa usuária pode agendar uma consulta conforme a necessidade da especialidade que ela procura. Essa é a tela inicial.

Voltando para o XCode, observamos que, nesse código, temos o HomeView e, a partir da linha 39, temos o body. Já sabemos que em SwiftUI, tudo que desenhamos na tela fica dentro do body. Nesse caso, temos um ScrollView uma imagem (Image()), os textos (Text()) e tudo que encontramos visualmente no simulador, representado em código.

Um ponto de atenção são os métodos da linha 17 e da linha 27, sucessivamente getSpecialists() e logout(). No getSpecialists(), fazemos um get dos especialistas, ou seja, fazemos uma requisição para buscar no servidor a lista de especialistas, e aguardamos uma resposta para mostrar isso no aplicativo. E o logout() é usado para pessoa usuária sair do aplicativo.

Esse é o primeiro ponto que eu queria começar a discutir com vocês. A ideia de arquivos do tipo view, em iOS, é apenas mostrar a parte visual para o usuário, como criar uma lista, colocar uma imagem, colocar uma etiqueta, enfim, todos os elementos visuais realmente ficam na view.

Porém, se começarmos a colocar outras responsabilidades nessa view, como chamadas HTTP, validação de regra de negócio, ou até mesmo validação de elementos visuais dentro da própria view, começamos a deixar essa view com muita responsabilidade e a longo prazo o nosso projeto fica mais difícil de escalar. Então, a ideia desse curso é começarmos a analisar a estrutura do que desenvolvemos e pensarmos como podemos melhorar isso, pensando no nosso projeto a longo prazo, em termos de escalabilidade e testabilidade.

Portanto, deixar chamadas HTTP em uma view talvez não seja a melhor solução. Também vamos analisar o arquivo WebService.swift. Esse arquivo possui todas as chamadas de APIs do aplicativo:

Portanto, o WebService.swift possui todas as chamadas para APIs. E quando o aplicativo crescer, ou seja, tiver mais telas, provavelmente terá mais métodos de requisição para o servidor, será que é interessante deixar tudo em um arquivo só? É isso que vamos discutir ao longo do curso: como melhorar essa organização e como dividir a responsabilidade das nossas classes para melhorar o acoplamento.

Vamos começar estudando o primeiro padrão de projeto que vamos aplicar, que se chama ViewModel. A ideia é tornar esse projeto MVVM (Model-View-ViewModel), que é um padrão arquitetural bem comum no desenvolvimento iOS. Há vários padrões, temos MVP, Viper e Clean Architecture, mas entre eles está o MVVM. Então, começaremos criando um ViewModel para separar essas responsabilidades da View.

Essa é a discussão inicial que eu queria trazer para vocês, de como podemos melhorar alguns pontos do nosso projeto, que são muito importantes de se pensar à medida que o projeto for crescendo. A partir do próximo vídeo, iniciaremos a criação do nosso primeiro ViewModel.

Analise de projeto - Criando o HomeViewModel

É hora de criarmos nosso primeiro ViewModel. Para isso, criaremos uma pasta chamada "ViewModels". Então, na coluna da esquerda, clicaremos com o botão direito na pasta "Vollmed", selecionaremos "New Group" (Novo Grupo), e escreveremos o nome de "ViewModels". Feito isso, arrastaremos a essa pasta para ficar abaixo de "Extensions".

Após movermos a pasta, criaremos um novo arquivo dentro dela. Então clicaremos com o botão direito em "ViewModels" e selecionamos "New File" (Novo Arquivo). Na janela que abre no centro da tela, selecionaremos a opção "Swift File" e clicaremos no botão, "Next", no canto inferior direito da tela. Na nova janela que se abre no centro da tela, nomearemos o arquivo como HomeViewModel e clicaremos no botão "Create", no canto inferior da janela.

As janelas abertas se fecham e um arquivo vazio, chamado HomeViewModel.swift, é aberto no XCode. Precisamos criar uma estrutura nesse arquivo, então, abaixo do import Foundation, criaremos a struct HomeViewModel{}. Voltaremos para "Views > Components > HomeView.swift" e, como comentamos no vídeo anterior, a ideia é começarmos refatorar esse arquivo, tirando as chamadas de APIs e colocando isso na pasta "ViewModels".

O primeiro método que vamos refatorar é o da linha 17: getSpecialists(), que usamos para buscar os especialistas. Abrindo o simulador, reparamos que é esse método que apresenta a de profissionais da medicina na Home. Portanto, vamos tirar da View a responsabilidade de realizar uma chamada HTTP. Teremos a referência do nosso HomeViewModel dentro da HomeView, e nele vamos colocar a lógica para fazer a chamada HTTP.

Sendo assim, voltaremos ao HomeViewModel que acabamos de criar. Primeiro, precisamos ter acesso ao arquivo WebService.swift, onde tem o método, de fato, que buscar as pessoas especialistas. Sendo assim, dentro da estrutura HomeViewModel criaremos um marcador para organizar nosso arquivo, escrevendo // MARK: - Attributes (MARCO: - Atributos). Na linha abaixo, criaremos uma referência que chamaremos de service para o Webservice. Que é a classe que tem todos os métodos de API do nosso projeto.

import Foundation

struct HomeViewModel {
    
    // MARK: - Attributes
    
    let service = WebService()
    
}

Com essa referência, já podemos criar os métodos de dentro do nosso ViewModel. Então, abaixo do service, escreveremos outro marco: // MARK: - Class methods (MARCO: - Métodos da classe). Esses marcos não fazem nada na execução de código, mas deixam nosso arquivo um pouco mais organizado. Essa é a sua função.

Embaixo desse marco, criaremos o método onde buscamos os especialistas. Para isso, retornaremos ao HomeView.swift, copiaremos o código da linha 17, com "Cmd + C", e colaremos abaixo do último marco do HomeViewModel.swift. A única diferença é que, antes de abrir chaves, escreveremos throws, que será mais uma anotação nesse método.

import Foundation

struct HomeViewModel {
    
    // MARK: - Attributes
    
    let service = WebService()
    
    // MARK: - Class methods
    
    func getSpecialists() async throws {
    
    }
    
}

O Throws (Lançar) significa que o método pode lançar uma exceção. Ou seja, caso ocorra algum problema na requisição, ele me devolverá um erro, e eu podemos manipular esse erro de alguma forma no aplicativo, como mostrar para o usuário ou realizar qualquer outra ação. Em contrapartida, quando implementarmos esse método na View, precisaremos utilizar aquela estrutura do do-catch, que é onde conseguimos fazer o tratamento caso ocorra um erro.

Sendo assim, dentro do getSpecialists(), chamaremos o do {} e, embaixo, o catch{}. Dentro do do, faremos uma verificação com if let fetchedSpecialists = try await service.getAllSpecialists() {}, ou seja, ele vai buscar os especialistas esperando a resposta do método getAllSpecialists do nosso serviço. Se ele conseguir obter algum valor, ele entrará nesse if e poderemos retornar essa variável que acabamos de criar, a fetchedSpecialists.

Caso ocorra um erro, ele cairá no catch, onde podemos imprimir uma mensagem, por exemplo, print("Ocorreu um problema para obter os especialistas"). E, na linha abaixo, exibimos o erro, através do throw error.

//código omitido

// MARK: - Class methods

func getSpecialists() async throws {
    do {
        if let fetchedSpecialists = try await service.getAllSpecialists() {
            return fetchedSpecialists
        }
    } catch {
        print("Ocorreu um problema para obter os especialistas")
        throw error
    }
}

Essa função precisa retornar a lista de especialistas que temos. Então, na declaração da função, após o throws, escreveremos -> [Specialist], que é uma lista, ou seja, um array, de especialistas. Com isso, finalizamos nosso método.

import Foundation

struct HomeViewModel {
    
    // MARK: - Attributes
    
    let service = WebService()

    // MARK: - Class methods

    func getSpecialists() async throws -> [Specialist] {
        do {
            if let fetchedSpecialists = try await service.getAllSpecialists() {
                return fetchedSpecialists
            }
        } catch {
            print("Ocorreu um problema para obter os especialistas")
            throw error
        }
    }

Agora precisamos utilizar esse método lá na HomeView, porém ainda aparece um erro no HomeViewModel porque, caso não consigamos obter a lista, esquecemos de pedir para ele retornar uma lista vazia. Então vamos corrigir isso após if. Caso não entre no if, não retorna nada.

import Foundation

struct HomeViewModel {
    
    // MARK: - Attributes
    
    let service = WebService()

    // MARK: - Class methods

    func getSpecialists() async throws -> [Specialist] {
        do {
            if let fetchedSpecialists = try await service.getAllSpecialists() {
                return fetchedSpecialists
            }
            
            return []
        } catch {
            print("Ocorreu um problema para obter os especialistas")
            throw error
        }
    }

Agora vamos voltar no arquivo HomeView.swift, onde já precisaremos ter uma referência do HomeViewModel. Logo no começo da estrutura HomeView, na linha 12, temos o service. A ideia é removê-lo quando terminarmos de refatorar, porque o HomeView vai mais receber nenhuma chamada para API. Também removeremos o authManager, que está na linha 13, porque não faz parte da View saber manejar o token do usuário.

Então, no próximo vídeo, usaremos o model que acabamos de criar.

Sobre o curso Swift: aplicando o padrão arquitetural MVVM e boas práticas de separação de responsabilidades

O curso Swift: aplicando o padrão arquitetural MVVM e boas práticas de separação de responsabilidades possui 123 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de iOS em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda iOS acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas