*Este post foi publicado originalmente (em inglês) no Cocoa Academy. Confira aqui.

Há algum tempo eu li um ótimo artigo, chamado “Architecture Wars”, no site da swifting.io. Em resumo, ele apresentava vários padrões de arquiteturas diferentes usados em iOS, prós e contras e etc. Arquitetura de software por si é um tópico enorme, com diversos sub-tópicos, mas ainda assim o artigo me inspirou e deu algumas ideias para explorar em posts futuros. Vou tentar demonstrar alguns desses padrões na prática, refatorando nosso velho e conhecido o app da Marvel (aquele que criamos nessa série de posts aqui e cujo repo você pode ver aqui).

A primeira arquitetura que vou falar sobre é a mais jovem de todas, o ReSwift, criada por Benjamin Encz e baseada no Redux. É uma implementação estilo Redux de uma arquitetura de fluxo de dados unidirecional em Swift. ReSwift pode te ajudar a resolver três importantes problemas dos componentes do seu app:

Estado: em ReSwift o estado do app inteiro é explicitamente armazenado em uma data struct. Isso ajuda a evitar código de gerenciamento de estado complicado, e permite debugar melhor com muito mais benefícios.

Views: em um app ReSwift as views se atualizam quando o estado muda. Suas views se tornam uma simples visualização do estado atual do app.

Mudanças de estado: em um app ReSwift você pode só performar mudanças de estado por meio de ações. Ações são pequenos pedaços de dados que descrevem uma mudança de estado. Limitando drasticamente a forma como o estado pode ser mudado, seu app fica mais fácil de entender e de trabalhar em conjunto com outros colaboradores – Reswift’s Github Page.

Tem um post excelente do Benjamin Encz falando sobre algumas das motivações por trás do ReSwift. Olha aqui.

Ao longo desse post vou mostrar algumas das modificações que eu fiz no meu app considerando a arquitetura ReSwift, algumas das lições aprendidas no processo e, claro, algumas opiniões pessoais =)

Já falei demais… Show me some code!

Estado

O estado da aplicação é definido em uma única estrutura simples de dados que deve ser uma struct. Essa struct pode ter outras structs como membros, o que te permite adicionar a sub-estados diferentes conforme seu app cresce. A primeira coisa que precisamos fazer é criar a AppState struct, e ela será responsável por manter todo o estado do aplicativo.

Nosso app é bem simples, então nossa struct só precisa saber de duas coisas:

– FavoritesState
– FetchedCharactersState

Como você pode ver, eu criei duas outras structs, conforme o protocolo StateType. A ideia aqui é que o AppState deve manter o estado de toda a aplicação, como mencionado, mas não por si só. Deve derivar o estado de muitas outras structs específicas de sub-estado, caso seja necessário.

Store

Nosso próximo ingrediente da receita é a Store, que será responsável por juntar tudo: estados, ações, reducers e etc. A Store vai ser usada para fazer subscribe/unsubscribe de elementos interessados em ser notificados sobre mudanças de estado, disparar actions que serão processadas pelos reducers, que por sua vez irão mudar o estado da aplicação.

A documentação e os exemplos de código recomendam o uso de uma variável localizada dentro do AppDelegate, chamada de store (veja o exemplo acima), para promover essa visibilidade global e singularidade, mas existem outras maneiras, como singletons, por exemplo.

Actions

Actions são usadas para expressar intenções de mudanças de estado. Ações não contêm funções, ao invés disso elas provêm informações sobre a intenção de mudança de estado, por exemplo “usuário precisa ser deletado”. No seu app ReSwift você define ações para cada mudança de estado possível que possa acontecer. Reducers lidam com essas ações e implementam mudanças de estado baseadas em uma informação que eles provêm. Todas as actions em ReSwift seguem o protocolo Action, que atualmente é só um marcador.

Reducers

Reducers são os monstros do trabalho, responsáveis por fazer todo o trabalho duro necessário para atualizar o estado do app. O app geralmente tem uma reducer principal que simplesmente delega o trabalho para outros mais específicos. Você pode ver abaixo alguns deles em ação.

Uma das vantagens principais de adotar ReSwift é que muito do que foi feito anteriormente dentro das suas views e views controllers pode ser migrado para reducers. O resultado final é bem interessante e permite que suas views e view controllers simplesmente disparem actions usando a store e atualizem a UI baseadas nas notificações de mudança de estado.

Você pode ver abaixo a mesma view com o mesmo comportamento antes e depois de adotar ReSwift.

Antes

Antes nossas views estavam usando RxSwift e falando diretamente com o Manager do Realm. Não é um design ruim, mas nossa view ainda sabe um pouco sobre os detalhes da implementação. Por exemplo, se pararmos de usar RxSwift ou Realm nós teríamos mudanças na view.

Uma View refatorada

Essa versão ReSwift removeu muitos detalhes da implementação. Agora nós só precisamos despachar algumas actions e fazer subscribe dos componentes interessados em mudanças de estado, e o comportamento ainda será o mesmo.

Conclusão

Essa foi minha primeira abordagem usando ReSwift, ainda tem muita coisa que não cobri aqui. A arquitetura tem outras extensões que oferece  capacidade de time travel,  routing e navegação. É uma arquitetura bem direta, e tenta acabar com o monstro da mutação, limitando o escopo de quando ela pode acontecer.

Dito isso, para mim a arquitetura poderia melhorar em alguns pontos, como:

– Exemplos ainda estão usando muitas funções globais;
– Deveria haver uma forma de fazer subscribe apenas para mudanças de sub-estado.Atualmente cada ação é primeiro tratada pelo Reducer principal, que delega isso para o reducer apropriado. Para as mudanças muito pequenas, uma nova struct AppState é criada, provocando notificações de mudanças de estado por todo o appe disparando notificações de mudança de estado para todos os subscribers;
– A variável Store dentro do AppDelegate tem que ser removida o quanto antes =X. Talvez um processo de inicialização como frameworks de analytics fazem… Não sei. Muitas coisas podem ser feitas para endereçar esse problema. Minha preocupação é mais um sentimento de que alguma coisa nesta variável soa estranha como se estivesse no lugar errado dentro do App Delegate.

Por último, mas não menos importante, eu realmente acho que você deveria sujar suas mãos com ReSwfit. É a única forma de achar a arquitetura certa pra você. Brinque com ela e compartilhe o que você achou aqui embaixo. Se você notar algo que pode ser melhorado no código e gostaria de ajudar, pull requests são mais que bem-vindos. Até a próxima!

É desenvolvedor iOS e gostaria de trabalhar em um time ágil de verdade? Clique aqui.

Sobre o Autor

Consultor especialista da Concrete Solutions, com grande experiência em mobile, desde apps nativas até híbridas e seus respectivos back-ends. Desenvolvedor iOS/Android e Ruby por hobby. Defensor de métodos ágeis e Lean Thinking, acredita que são uma maneira de sintetizar um processo com passos bem definidos para trazer de volta o bom senso para a indústria de software.

cases

rio de
janeiro

Rua São José, 90
Sala 2121 - Centro
(21) 2240-2030
CEP: 20010-020

são
paulo

Av. Nações Unidas
nº 11.541 - 3º andar
(11) 4119-0449
CEP: 04578-000

belo
horizonte

Av. Getúlio Vargas, 671
Sala 800 - 8º andar
(31) 3360-8900
CEP: 30112-021