Como estruturar sua camada de network usando Moya

Vitor Ferraz Varela
7 min readJul 31, 2019

--

Bom, acredito que se você está lendo esse artigo provavelmente deve estar pensando em formas de estruturar sua camada de network e resolver todos os problemas.

Desculpe te desapontar, esse artigo não é uma bala de prata e não vai resolver todos seus problemas, meu objetivo é dar uma alternativa para você poder criar sua própria camada baseado em pontos que acho importante e que toda camada de network deve ter.

Acredito que todo mundo já trabalhou em um projeto ou viu tutoriais com uma classe APIManager, NetworkManager ou algo do tipo, onde todas as chamadas do aplicativo estão sendo feitas, com cerca de mil linhas dependendo do projeto né?

Seja usando URLSession ou Alamofire, esse tipo de abordagem torna o projeto de difícil manutenção porque você não tem uma separação por contextos da sua aplicação, não é fácil de se testar, possui repetição de código e não tem separação de responsabilidades, além de criar um forte acoplamento o que torna difícil expandir funcionalidades sem modificá-las.

Pensando nisso veio o Moya, que é uma abstração em cima do Alamofire, que encapsula as chamadas em uma estrutura de tipagem segura (type-safe structure) baseado em enums visando o maior controle e configuração das chamadas. Uma das coisas bacanas do Moya é que foi desenvolvido pensando em testes e na expansão de funcionalidade utilizando plugins, como de autenticação e logs.

https://github.com/Moya/Moya

Vamos entender como o Moya funciona e alguns conceitos chaves sobre sua estrutura. Bem como vimos, o Moya é uma abstração em cima do Alamofire que é utilizado por baixo dos panos para criar as estrutura de request que irão se comunicar com a internet.

Alguns conceitos importantes sobre o Moya antes da gente implementar no código:

  • Providers: É o principal objeto que você irá criar para interação com qualquer service de network (Services são objetos que usamos para criar e preparar toda nossa chamada para serviços web);
  • Target: Cada target representa sua service e tudo que é necessário para realizar uma request, como as informações das chamadas: endpoints, métodos https, payload etc;
  • Endpoint: Os providers transformam cada target em um endpoint que eventualmente é transformado em um URLRequest.
https://www.raywenderlich.com/5121-moya-tutorial-for-ios-getting-started

Para esse projeto vamos utilizar o aplicativo Carangas, retirado do curso do Eric Brito, resumidamente é um aplicativo que podemos listar, cadastrar e editar um carro.

Primeiro vamos criar nosso o Target:

Bem simples né? Um enum com cada chamada que o aplicativo deve fazer. Cada “case” do enum será um tipo de endpoint diferente.

Como todas as chamadas estão relacionadas ao mesmo contexto de carro, podemos deixar todas no mesmo enum, mas caso existam chamadas que sejam de contextos diferentes é uma boa prática criar um novo enum para cada contexto.

Agora vamos implementar o protocolo TargetType

Esse protocolo define um conjuntos de propriedades que devemos configurar para o nosso Target.

Agora vamos entender cada uma das propriedades:

  • baseURL: É a url base do seu serviço. Neste caso temos duas URLs base uma para se comunicar com o heroku, para buscar os carros cadastrados e outra para se comunicar a API do FIPE;
  • path: São os diferentes endpoints que podemos chamar na aplicação. Por exemplo “/car” , “/car/{id}”, etc;
  • method: É o método HTTP (GET, PUT, DELETE, POST);
  • sampleData: É um objeto de dado que pode ser usado para fazer chamadas mockadas;
  • task: É a tarefa da sua chamada HTTP, onde podemos passar dados para a nossa API. O bacana é que o Moya possui diversos tipos diferentes dentro desse enum para tratar cenários de uma forma bem customizada e prática;
  • headers: São os cabeçalhos da requisição;
  • validationType: Aqui é onde definimos os ranges de status code de sucesso e erro.

Vamos entender um pouco mais sobre os tipos de tasks

Quando estamos comunicando com a nossa API, ela pode esperar diversos tipos de dados, seja uma imagem, um JSON entre outro tipos. Desta forma, o Moya possui diversos tipos de taks que nos possibilita configurar esse tipo de informação.

  1. requestPlain: Usado para requests que não requer passar nenhum tipo de dado. Como por exemplo um simples “get”;
  2. requestData: Usado quando a request espera que um objeto do tipo “data” seja passado no body da request;
  3. requestJSONEncodable: Usado quando a request espera um objeto JSON encodable;
  4. requestParameters: Usado quando queremos passar os parâmetros e definir o nosso próprio tipo de encoding (Por exemplo um URLEncoding.queryString: https://api.themoviedb.org/3/movie/new_plaing?api_key=898&language=en-US&page1);
  5. requestCompositeData: Usado quando você quer passar um objeto do tipo data como body da request e também informar os parâmetros da URL;
  6. requestCompositeParamers: Igual o requestCompositeData mas passando parâmetros no body ao invés de um objeto do tipo “data”;
  7. downloadParameters: Usado para baixar algum aquivo e passar parâmetros se necessário;
  8. uploadCompositeMultipart: Usado para passar dados tipo multipart com parâmetros.

Ah, antes de prosseguir, falta um arquivo que temos que criar para separar as URLs e cabeçalhos dos nossos serviços.

Pronto, agora que temos o nosso primeiro target criado e nosso arquivo de constantes sobre as URLs podemos entrar no conceito de repositório.

O pattern repository prove solucionar o problema de buscar dados em lugares diferentes, abstraindo por meio de um protocolo, que é comum entre as diversas classas que irão recuperar os dados. Por exemplo, os detalhes de implementação do core data, realm ou a camada de network ficam restritos a suas classes mas a comunicação é feita por meio do protocolo, e assim fica transparente para a sua aplicação.

Mãos à obra, vamos criar nosso protocolo

Note que vamos usar uma closure com um enum Result que veio no Swift 5, que possui um caso de success e failure. No caso de failure vamos criar um enum que deve conformar ao protocolo de error.

Bacana, agora vamos implementar nosso protocolo

No início eu tinha falado que o Moya é composto por um target, que já criamos, e um provider que irá converter o target em uma chamada.

Agora iremos criar um motor para nossas chamadas onde os providers são executados de acordo com um target específico, fazendo o decoding do data retornado na chamada e devolvendo o nosso objeto esperado.

  1. Bom, primeiro estamos criando um provider passando um Target genérico que definimos. Desta forma não ficamos restritos somente ao CarTarget, podemos criar vários outros tipos de targets e passar para o nosso motor;
  2. Vamos fazer a construção deste provider por meio de uma injeção de dependência no construtor da classe, desta forma podemos passar um provider específico que o Moya tem para testes que iremos ver mais para frente;
  3. No request é o momento que de fato estamos realizando a chamada para nossa API de acordo com um target específico;
  4. Neste momento é onde tentamos decodificar o nosso data retornando na response da chamada para um objeto genérico T que é do tipo codable. Assim podemos passar qualquer tipo de objeto contanto que ele conforme ao protocolo Codable;
  5. Por último, esse requestVoid foi criado porque alguns tipos de chamadas, dependendo da forma que sua API foi estruturada, pode não retornar nenhum objeto. Por exemplo um DELETE, um PUT, ou até mesmo um GET de uma lista pode retornar um 204-No content. Então por uma questão de semântica vamos ter um request específico para chamadas que não retornam nada.

Pronto, criamos nosso motor que irá fazer as chamadas, agora temos que implementar ele dentro do nosso repositório.

Dica: Ás vezes queremos debugar nossas chamadas de uma forma simples e rápida e poder ver tudo que está acontecendo debaixo dos panos. Os providers do Moya podem estender suas funcionalidades por meio de plugins, adicionando por exemplo NetworkLoggerPlugin, onde podemos ver todos os detalhes de nossa chamadas, como o cURL, o retorno e outros detalhes, basta adicionar o plugin desta maneira:

Agora para realizar nossas chamadas de API, basta a view model receber um repositório desse modo:

Caso a gente queira testar nossos repositórios, o Moya possui um provider específico para essa funcionalidade. Implemente o protocolo de CarRepository e passe no construtor do motor do request um MoyaProvider do tipo stubClosure.

Podemos criar um stub com um delay de tempo passando um TimeInterval:

MoyaProvider.delayedStub(1.0)

Ou um stub que retorne nosso objeto de forma imediata:

MoyaProvider.immediatelyStub

Com isso, nossa classe de testes ficará da seguinte forma:

Bom, dentro do nosso CarTarget, existe uma propriedade que faz parte do protocolo do TargetType, chamada sampleData, usada para prover um objeto de stub do tipo data usado para testes.

“Provides stub data for use in testing.”

Basta arrastar para seu projeto o arquivo JSON que representa seus dados e lê-los retornando um objeto do tipo "data".

Dica: Para facilitar a leitura de arquivos JSON criei essa extension que pode te ajudar.

--

--

Vitor Ferraz Varela
Vitor Ferraz Varela

Responses (1)