Patrón MVVM en el desarrollo de iOS: progreso práctico (continuación)
MVVM (Model View ViewModel) es una variante de MVC (Model View Controller), que se utiliza para resolver el problema de los controladores grandes y complejos en MVC que son difíciles de mantener. En términos generales, MVVM tiene varios requisitos:
MVVM y MVC tienen muchas características similares, las principales diferencias son:
Por otro lado, los valores predeterminados de MVVM entre vistas y controladores de vistas. relación uno a uno. Generalmente, consideramos a estos dos como un todo y pensamos en ellos como. archivos rápidos y guiones gráficos.
El trabajo del modelo de vista es manejar toda la lógica para mostrar datos. Si hay un objeto NSDate en el modelo, NSDateFormatter se utilizará para establecer el formato de visualización de la fecha en el modelo de vista.
Los modelos de vista no pueden tocar ninguna parte de la interfaz de usuario y los archivos de modelo de vista no deben importarse a UIKit. El controlador de vista observará el modelo de vista para saber cuándo mostrar nuevos datos (ya sea a través de KVO o FRP (Programación reactiva funcional)).
MVVM tiene la misma debilidad que MVC: no hay una definición clara de dónde colocar la parte de solicitud de red. En la práctica, colocaría la solicitud de red en el archivo del modelo de vista, pero luego planeé colocar la solicitud de red en su propia clase independiente, y el archivo del modelo de vista tendría este objeto.
A continuación se habla principalmente de algunos desafíos de MVVM en aplicaciones prácticas:
Por ejemplo, si desea construir una interfaz universal de este tipo, hay un control de segmento en la parte superior de la pantalla, y otras partes de la pantalla es una vista de colección. Al seleccionar diferentes fragmentos se mostrarán diferentes estilos de vistas de colección y el orden en el que están organizados los elementos. Definimos una enumeración para enumerar todos los estilos de disposición:
Entonces, en modo MVVM, ¿dónde se debe colocar esta enumeración? Debido a que esta enumeración determina el orden en que se organizan los datos, y el texto de cada celda y el título del botón pertenecen a la lógica de visualización, parece que esta enumeración debería colocarse en el modelo de vista.
Sin embargo, estos diseños no cambian los datos que se mostrarán, sino que solo determinan la disposición y el orden de los datos que se mostrarán. Desde esta perspectiva, la enumeración debe colocarse en el controlador de vista.
Mi solución fue colocar la enumeración en el modelo de vista y luego agregar un observable externo o una señal en el modelo de vista para indicar qué diseño usar. Según el segmento seleccionado por el usuario, el modelo de vista actualiza este valor. Luego, se aplica un estilo a la vista de la colección según el diseño correspondiente en el controlador de vista, y el controlador de vista también puede decidir qué identificador de reutilización de celda usar en función de este valor.
Quizás la pregunta más común para los desarrolladores de iOS al escribir aplicaciones con MVVM y FRP es cómo ViewModel presenta los datos al ViewController. Cuando los datos en la capa del modelo cambian y se actualizan, se debe notificar al ViewController y luego se actualiza la interfaz de usuario correspondiente. Generalmente usamos dos mecanismos:
La primera opción es atractiva porque puedes decidir cómo eliges observar esas propiedades en el controlador de vista. Sin embargo, no recomiendo usar la primera opción en Swift porque Swift no tiene verificación de tipos en KVO y necesitas convertir el tipo de AnyObject varias veces.
La segunda opción es comparar Swift. Según la función genérica de Swift, las señales, secuencias y observables pueden admitir la verificación de tipos en tiempo de compilación.
Pero a veces es difícil agregar estas señales u observables al modelo de vista. El inicializador de Swift tiene reglas muy claras sobre cuándo asignar valores a las propiedades. Las señales u observables necesitan usar el estado dentro del modelo de vista, por lo que solo se pueden crear después de super.init(), pero por otro lado, garantizamos que todas las propiedades se hayan asignado antes de llamar a super.init(), incluidas aquellas señales/Propiedades observables.
Esta es la pregunta del huevo o la gallina.
Adopto una solución más simple: definirlo como un tipo opcional implícito de var, de modo que a la propiedad se le pueda asignar un valor después de super.init(). Ésta no es una solución perfecta.
Podemos reemplazar el método anterior con la asignación de cierre del atributo var diferido. A medida que Swift continúa mejorándose y actualizándose, existen otras formas mejores de explorar.
Para dar un ejemplo muy común, el usuario hace clic en una celda en la vista de colección y salta a la página de detalles. La operación de clic del usuario debe manejarse en el controlador de vista y el contenido específico debe mostrar una nueva página de detalles. Pero el controlador de vista no puede tocar el modelo directamente. ¿Cómo implementar dicha interacción del usuario utilizando el patrón MVVM?
Mi solución es utilizar cierres Swift. Primero, defina un cierre en el modelo de vista:
Luego agregue la propiedad al modelo de vista:
Luego necesito llamar al cierre, definir un controlador de vista en el modelo de vista Funciones eso se puede llamar. Los parámetros de esta función determinan qué datos se utilizan. En términos generales, la ruta de índice se usa comúnmente:
Ahora, cuando el usuario selecciona una celda, se llamará a esta función en el modelo de vista y se pasará el parámetro de ruta de índice. El modelo de vista decide qué datos usar y llama a los cierres definidos en el controlador de vista, por ejemplo:
La pregunta final es cómo crear este modelo de vista. Necesitamos pasar un cierre al inicializador del modelo de vista y luego usar la carga diferida para llamar al inicializador del modelo de vista.