Escrevendo aplicativos iOS usando o padrão Redux

imagem

Recentemente, estive mais envolvido no desenvolvimento front-end do que em dispositivos móveis, e me deparei com alguns padrões de design muito interessantes que eu já conhecia, mas que não os conheci ... até agora.

Mas agora tudo isso faz sentido, depois de usar o desenvolvimento no React por várias semanas, agora não posso voltar às minhas antigas formas de desenvolvimento para iOS. Não mudarei para o javascript (AKA React Native) para desenvolver aplicativos móveis, mas aqui estão algumas coisas que aprendi.

imagem

Voltando ao desenvolvimento do iOS, criei um novo projeto e comecei a explorar o ReSwift , que é uma implementação do padrão Flux e Redux no Swift. E funciona de maneira simples: clonei a arquitetura do aplicativo JavaScript várias vezes, agora tenho um estado global e meus controladores apenas ouvem esse estado. Os próprios controladores são compostos de vários componentes de apresentação que encapsulam um comportamento muito específico.

imagem

Todas as alterações de estado são feitas em um só lugar, no redutor . Um para o subestado. Você pode ver todas as ações em um só lugar. Não há mais código de rede ou controladores de chamada, não há mais mutações de objetos nas visualizações. Não há mais código de espaguete. Existe apenas um estado , e é verdade, então seus vários componentes da apresentação (e eu insisto) se inscrevem em diferentes partes do estado e reagem de acordo. Esta é simplesmente a melhor arquitetura para um aplicativo de modelo forte.

Por um exemplo. Anteriormente, os controladores de exibição de logon eram preenchidos com muitas linhas de código, vários estados de controle, tratamento de erros, etc. ... Agora parece com isso: (Como exemplo)

import UIKit import Base import ReSwift class LoginViewController: UIViewController { @IBOutlet var usernameField: UITextField! @IBOutlet var passwordField: UITextField! override func viewDidLoad() { super.viewDidLoad() store.subscribe(self) {state in state.usersState } } @IBAction func onLoginButton(_ sender: Any) { store.dispatch(AuthenticatePassword(username: usernameField.text!, password: passwordField.text!)) } @IBAction func onTwitterButton(_ sender: Any) { store.dispatch(AuthenticateTwitter()) } @IBAction func onFacebookButton(_ sender: Any) { store.dispatch(AuthenticateFacebook(from: self)) } } // MARK: - State management extension LoginViewController: StoreSubscriber { func newState(state: UsersState) { if let error = state.authState.error { presentError(error: error.type, viewController: self, completion: nil) } if let _ = state.getCurrentUser { self.dismiss(animated: true, completion: nil) } } } 

Controladores e representações de ações de despacho no estado global, essas ações realmente funcionam com a rede ou iniciam as várias partes que seu aplicativo precisará converter para o novo estado.

Uma ação pode acionar outra ação, é assim que ocorre para uma solicitação de rede, por exemplo, você tem uma ação FetchUser (id: String) e uma ação que você intercepta em um redutor que se parece com SetUser (usuário: Usuário). No redutor, você é responsável por mesclar / mesclar um novo objeto com seu estado atual.

Primeiro você precisa do estado , meu exemplo se concentrará no objeto Usuário , portanto, o estado pode ser algo como isto:

 struct UsersState { var users: [String: User] = [:] } 

Você deve ter um arquivo que encapsule todas as atividades de rede para o objeto de usuário.

 struct FetchUser: Action { init(user: String) { GETRequest(path: "users/\(user)").run { (response: APIResponse<UserJSON>) in store.dispatch(SetUser(user: response.object)) } } } 

Assim que a solicitação é concluída, ela chama outra ação , esta ação está realmente vazia, deve ser referenciada, por exemplo, em UsersActions. Esta ação descreve o resultado no qual o redutor deve confiar para alterar o estado.

 struct SetUser: Action { let user: UserJSON? } 

E, finalmente, o trabalho mais importante é realizado no UsersReducer , você precisa capturar a ação e executar alguns trabalhos de acordo com seu conteúdo:

 func usersReducer(state: UsersState?, action: Action) -> UsersState { var state = state ?? initialUsersState() switch action { case let action as SetUser: if let user = action.user { state.users[user.id] = User(json: user) } default: break } return state } 

Agora, tudo o que é necessário é assinar / assinar o estado em controladores ou visualizações e, quando ele muda, extrai as informações necessárias e obtém novos valores!

 class UserViewController: UIViewController { var userId: String? { didSet { if let id = userId { store.dispatch(FetchUser(user: id)) } } } var user: User? { didSet { if let user = user { setupViewUser(user: user) } } } override func viewDidLoad() { super.viewDidLoad() store.subscribe(self) {state in state.usersState } } func setupViewUser(user: User) { //Do uour UI stuff. } } extension UserViewController: StoreSubscriber { func newState(state: UsersState) { self.user = state.users[userId!] } } 

Mas agora você deve dar uma olhada nos exemplos do ReSwift para um entendimento mais profundo, pretendo publicar um aplicativo de código aberto (na verdade um jogo) usando esse padrão de design. Mas, por enquanto, o código exibe uma ideia muito grosseira de como tudo isso funciona em conjunto.

Essa ainda é uma arquitetura muito antiga nos livros da Glose, mas não podemos esperar que o aplicativo seja colocado em produção usando essa arquitetura.

Eu sinto que o desenvolvimento de aplicativos usando esse padrão economizará muito tempo e esforço. Vai demorar um pouco mais de trabalho do que um cliente REST estupidamente simples, porque haverá um pouco mais de lógica dentro do estado do cliente, mas no final, você economizará um tempo inestimável para depuração. Você poderá modificar muitos elementos localmente e não haverá mais alterações em cascata entre controladores e exibições. Reproduza o estado na ordem de backup, arquive-o, crie middleware etc. O fluxo de dados do aplicativo é claro, centralizado e simples.

O padrão Redux adiciona um pouco de estrutura ao aplicativo. Estou fazendo MVC puro há muito tempo, tenho certeza de que você pode criar uma base de código limpa, mas tende a desenvolver hábitos que geralmente causam mais danos do que benefícios. Você pode dar um passo adiante e implementar completamente o Redux controlando sua interface do usuário (como exibir controladores, visualizadores de alerta, controladores de roteamento) em um estado separado, mas ainda não consegui tudo isso.

E os testes ... Agora, o teste de unidade é fácil de implementar, porque tudo que você precisa testar é comparar os dados inseridos com os que estão contidos no estado global, para que os testes possam enviar ações simuladas e depois verificar se o estado corresponde ao que você deseja. quer.

Sério, esse é o futuro. O futuro é para o Redux :)

Source: https://habr.com/ru/post/pt464561/


All Articles