Escribir aplicaciones iOS usando el patr贸n Redux

imagen

Recientemente, he estado m谩s involucrado en el desarrollo front-end que en los dispositivos m贸viles, y me encontr茅 con algunos patrones de dise帽o muy interesantes que ya conoc铆a, pero que realmente no profundic茅 en ellos ... hasta ahora.

Pero ahora todo esto tiene sentido, despu茅s de usar desde el desarrollo en React durante varias semanas, ahora no puedo volver a mis viejas formas de desarrollo para iOS. No cambiar茅 a javascript (AKA React Native) para desarrollar aplicaciones m贸viles, pero aqu铆 hay algunas cosas que aprend铆.

imagen

Volviendo al desarrollo de iOS, cre茅 un nuevo proyecto y comenc茅 a explorar ReSwift , esta es una implementaci贸n del patr贸n Flux y Redux en Swift. Y funciona de manera bastante simple, clon茅 la arquitectura de la aplicaci贸n JavaScript varias veces, ahora tengo un estado global y mis controladores solo escuchan este estado. Los controladores mismos est谩n formados por varios componentes de presentaci贸n que encapsulan un comportamiento muy espec铆fico.

imagen

Todos los cambios de estado se realizan en un solo lugar, en reductor . Uno para el subestado. Puedes ver todas las acciones en un solo lugar. No m谩s c贸digo de red o controladores de llamada, no m谩s mutaciones de objetos en las vistas. No m谩s c贸digo de espagueti. Solo hay un estado , y es cierto, sus diversos componentes de presentaci贸n (e insisto en ello) se suscriben a diferentes partes del estado y reaccionan en consecuencia. Esta es simplemente la mejor arquitectura para una aplicaci贸n de modelo fuerte.

Por un ejemplo. Anteriormente, los controladores de vista de inicio de sesi贸n estaban llenos de muchas l铆neas de c贸digo, varios estados de control, manejo de errores, etc. Ahora se ve as铆: (Como ejemplo)

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 y representaciones de acciones de despacho en el estado global, estas acciones realmente funcionan con la red o lanzan las diversas partes que su aplicaci贸n necesitar谩 convertir al nuevo estado.

Una acci贸n puede desencadenar otra acci贸n, as铆 es como sucede para una solicitud de red, por ejemplo, tiene una acci贸n FetchUser (id: String) y una acci贸n que intercepta en un reductor que se parece a SetUser (usuario: Usuario). En reductor, usted es responsable de fusionar / fusionar un nuevo objeto con su estado actual.

Primero necesita estado , mi ejemplo se centrar谩 en el objeto Usuario , por lo que el estado podr铆a verse as铆:

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

Debe tener un archivo que encapsule todas las actividades de red para el objeto de usuario.

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

Tan pronto como se completa la solicitud, se llama a otra acci贸n , esta acci贸n est谩 realmente vac铆a, debe mencionarse, por ejemplo, en UsersActions. Esta acci贸n describe el resultado en el que el reductor debe confiar para cambiar de estado.

 struct SetUser: Action { let user: UserJSON? } 

Y, por 煤ltimo, el trabajo m谩s importante se realiza en UsersReducer , debe tomar la acci贸n y hacer un trabajo de acuerdo con su contenido:

 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 } 

Ahora todo lo que se necesita es suscribirse / suscribirse al estado en controladores o vistas, y cuando cambie, extraer la informaci贸n necesaria y obtener nuevos 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!] } } 

Pero ahora deber铆a echar un vistazo a los ejemplos de ReSwift para una comprensi贸n m谩s profunda, planeo publicar una aplicaci贸n de c贸digo abierto (en realidad un juego) usando este patr贸n de dise帽o. Pero por ahora, el c贸digo muestra una idea muy cruda de c贸mo funciona todo esto en conjunto.

Esta es todav铆a una arquitectura muy temprana en los libros de Glose, pero no podemos esperar a que la aplicaci贸n se ponga en producci贸n usando esta arquitectura.

Siento que desarrollar aplicaciones que usen este patr贸n ahorrar谩 mucho tiempo y esfuerzo. Tomar谩 un poco m谩s de trabajo que un cliente REST est煤pidamente simple, porque habr谩 un poco m谩s de l贸gica dentro del estado del cliente, pero al final le ahorrar谩 un tiempo invaluable para la depuraci贸n. Podr谩 modificar muchos elementos localmente y ya no habr谩 cambios en cascada entre los controladores y las vistas. Reproduzca el estado en orden de copia de seguridad, arch铆velo, cree middleware, etc. El flujo de datos de la aplicaci贸n es claro, centralizado y simple.

El patr贸n Redux agrega un poco de estructura a la aplicaci贸n. He estado haciendo MVC puro durante mucho tiempo, estoy seguro de que puedes crear una base de c贸digo limpia, pero tiendes a desarrollar h谩bitos que a menudo hacen m谩s da帽o que bien. Incluso puede dar un paso m谩s e implementar completamente Redux controlando su interfaz de usuario (como controladores de vista, visores de alertas, controladores de enrutamiento) en un estado separado, pero a煤n no he logrado todo esto).

Y pruebas ... Las pruebas unitarias ahora son f谩ciles de implementar, porque todo lo que necesita probar es comparar los datos que ingresa con los datos contenidos en el estado global, para que las pruebas puedan enviar acciones simuladas, y luego verificar si el estado coincide con lo que usted querer

En serio, este es el futuro. El futuro es para Redux :)

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


All Articles