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铆.
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.

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)) } }
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) {
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 :)