A elegância dos tipos associados nos protocolos do Swift posta em uso com a implementação de um módulo para execução de requisições para o back end

Elias Medeiros
CWI Software
Published in
3 min readAug 24, 2020

Talvez você já saiba da utilidade de tipos genéricos para melhor aproveitar a lógica de uma função ou as capacidades de uma classe. Caso negativo, dê uma olhadinha por aí.

Quando implementamos uma classe genérica, demarcamos uma lacuna a ser preenchida por um tipo. O código que consumir nossa classe genérica será responsável por decidir que tipo será esse.

Num protocolo Swift esta lacuna é definida usando um tipo associado. E cada implementação que conformarmos a este protocolo deverá decidir e indicar este tipo.

Não sei se ficou claro, mas no título comentei sobre um módulo de execução de requisições. Nesta publicação, me baseio na experiência que tive de comunicação com um back end com diversas APIs, e o objetivo do design utilizado para este módulo é facilitar a organização dos endpoints disponíveis para cada API, agrupando-os em implementações diferentes do nosso requester, porém sem precisar reescrever a lógica que executa as chamadas.

Vamos começar vendo como declarar o uso de um tipo associado e como defini-lo.

exemplo I

Barbada! Agora, quando estamos desenhando um protocolo, pensamos nos requisitos ou nas capacidades que seus conformantes devem ter. Um requester deve ter a capacidade de fazer requests. Além do endpoint desejado, vou adicionar mais alguns parâmetros para nosso exemplo ficar mais realista e criar a especificação.

exemplo II

Aqui eu tomei a liberdade de usar dois typealias, a fim de dar aquela enxugada na assinatura do método. Não vamos nos preocupar com o Result por enquanto. Mas, se você olhar atentamente, há de perceber que criei um novo protocolo e defini que o tipo associado Endpoint deve se conformar ao EndpointProvider. O objetivo disso daí é muito claro: teremos uma lógica padrão que consumirá a propriedade url que o nosso endpoint é obrigado a fornecer. Um efeito dessa alteração em conjunto com aquela struct simplória do exemplo I é que a exigência do protocolo não é mais atendida, então precisamos arrumar isso. Já aproveitamos pra introduzir um outro elemento fundamental para elevação do garbo da nossa arquitetura: enum. Veja.

exemplo III

Dessa forma, nosso UserBackendRequester atende às exigências do BackendRequester e ganhamos esta benesse de valor incalculável:

olha esse autocomplete!

A partir daí, podemos focar numa implementação genérica da requisição, que seja reaproveitada por quem se conformar ao nosso protocolo. Para isso basta fazer uma extensão do BackendRequester, adicionando uma implementação padrão — isso é o mais próximo que o Swift chega de uma classe abstrata.

exemplo IIII

O foco aqui é no aproveitamento do associatedtype, então não vamos entrar em detalhes sobre a implementação da request. Nesta última versão, estamos usufruindo de uma variável que definimos como exigência para nosso Endpoint associado. Isso poderia ser expandido para mais atributos relacionados ao nosso endpoint, como o contentType a ser enviado como header. Ao final, eu deixarei um link do GitHub com todo o conteúdo desta publicação.

Por agora, chegamos ao ponto principal desta história toda, que é expandir para novas API’s sem precisar de mais implementações. Suponhamos que nosso back end tenha também uma consulta de produtos.

exemplo IIIII

Nosso UserBackendRequester está simplíssimo, sem lógica alguma, pois ele é atendido pela implementação padrão feita em BackendRequester. Já no ProductBackendRequester, fizemos uso do valor associado do enum para receber o código de detalhe pesquisado. Infelizmente, isso faz com que não seja mais possível usar unicamente um rawValue para identificar nosso endpoint, então tive que fazer aquele switch na varíavel url.

O fato de termos usado um protocolo como cerne da nossa arquitetura nos permite substituir uma implementação verídica por um mock para testes, por exemplo. Além disso, alterações de lógica só serão necessárias em um lugar, o resto é basicamente configuração.

A soma de todos os códigos desta publicação está disponível em: https://github.com/eliaspmedeiros/backend-requester/blob/master/Requester.swift

--

--

Elias Medeiros
CWI Software

Enfileirar palavras é meu ofício; algumas vezes em português, outras em Swift ou Kotlin :)