Verwendung von MVVM in iOS
Ich bin ein iOS-Entwickler, und ich bin schuldig Massive View-Controller in meinen Projekten so, ich habe die Suche nach einem besseren Weg, um Struktur meiner Projekte und bin auf das MVVM (Model-View-ViewModel-Architektur. Ich habe schon viel gelesen von MVVM mit iOS, und ich habe ein paar Fragen. Ich werde erklären, meine Fragen mit einem Beispiel.
Ich habe einen view-controller genannt LoginViewController
.
LoginViewController.swift
import UIKit
class LoginViewController: UIViewController {
@IBOutlet private var usernameTextField: UITextField!
@IBOutlet private var passwordTextField: UITextField!
private let loginViewModel = LoginViewModel()
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func loginButtonPressed(sender: UIButton) {
loginViewModel.login()
}
}
Es nicht über eine Model-Klasse. Aber ich habe eine Ansicht erstellen, Modell namens LoginViewModel
um die Validierungs-Logik und Netzwerk-Aufrufe.
LoginViewModel.swift
import Foundation
class LoginViewModel {
var username: String?
var password: String?
init(username: String? = nil, password: String? = nil) {
self.username = username
self.password = password
}
func validate() {
if username == nil || password == nil {
//Show the user an alert with the error
}
}
func login() {
//Call the login() method in ApiHandler
let api = ApiHandler()
api.login(username!, password: password!, success: { (data) -> Void in
//Go to the next view controller
}) { (error) -> Void in
//Show the user an alert with the error
}
}
}
-
Meine erste Frage ist einfach ist mein MVVM Implementierung korrekt? Ich habe diese Zweifel, weil zum Beispiel ich die login-Schaltfläche Tippen Sie auf ein Ereignis (
loginButtonPressed
) in den controller. Ich wusste nicht, erstellen Sie eine separate Ansicht für den login-Bildschirm, weil es hat nur ein paar Textfeldern und einem button. Ist es akzeptabel für den controller zu haben, der Ereignis-Methoden gebunden zu UI-Elementen? -
Meine nächste Frage ist auch über den login-button. Wenn der Benutzer auf die Schaltfläche, die die Werte username und password sollten gte ging in die LoginViewModel für die Validierung und wenn erfolgreich, dann kann der API-Aufruf. Meine Frage, wie übergeben Sie die Werte an den view-Modell. Sollte ich zwei Parameter Hinzugefügt, um die
login()
- Methode auf und übergeben Sie, wenn ich rufen Sie es von der view-controller? Oder soll ich erklären Eigenschaften in der Ansicht Modell und legen Sie deren Werte aus der view controller? Welche akzeptabel ist in MVVM? -
Nehmen die
validate()
Methode im view-model. Die Nutzer sollten benachrichtigt werden, wenn Sie leer sind. Das bedeutet, dass nach der überprüfung, das Ergebnis zurückgegeben werden soll, um den view-controller auf, die notwendigen Maßnahmen (anzeigen einer Meldung). Gleiche Sache mit denlogin()
Methode. Den Nutzer warnen, wenn die Anforderung fehlschlägt oder gehen Sie zum nächsten view-controller, wenn es gelingt. Wie kann ich benachrichtigt den controller der diese Ereignisse aus der view-Modell? Ist es möglich, verbindliche Mechanismen wie der KVO in Fällen wie diesem? -
Was sind die anderen bindungsmechanismen bei der Verwendung von MVVM für iOS? KVO ist eine. Aber ich lese es nicht ganz geeignet für größere Projekte, weil es erfordert eine Menge boilerplate code (registrieren/deregistrieren Beobachter etc). Was sind andere Optionen? Ich weiß ReactiveCocoa ist ein framework, das verwendet wird für das, aber ich bin auf der Suche, um zu sehen, ob es irgendwelche anderen einheimischen.
Alle Materialien stieß ich auf MVVM im Internet wenig bis keine Informationen über diese Teile bin ich auf der Suche, um zu klären, also ich würde wirklich zu schätzen Ihre Antworten.
- Ist das nur bei mir oder jemand anderem nicht wie Netzwerk-Anforderungen, die von einem view-Modell zu?
- Ich Stimme zu, es ist am besten Praxis nicht zu machen, Netzwerk-Aufrufe in der Sicht-Modell, aber in dem Beispiel die ApiHandler Klasse erfolgreich abstrahiert die Besonderheiten auf, wie eine Anmeldung durchgeführt wird. Es ist nur eine Vermutung an dieser Stelle, dass es in der Tat eine Netzwerk-Anruf stattfindet. Die app könnte offline sein und die Anmeldung über eine lokale db. Wir wissen nicht und auch nicht das view-Modell (das ist, wie es sein sollte). Es wäre besser, wenn der Datentyp für die api-variable wurde ein Protokoll, das implementiert wurde, durch ApiHandler.
Du musst angemeldet sein, um einen Kommentar abzugeben.
waddup dude!
1a - Du bist auf dem richtigen Weg. Sie setzen loginButtonPressed in der view-controller und das ist genau wo es sein sollte. Event-Handler für die Kontrollen sollte immer gehen in der view-controller - so ist das richtig.
1b - aus Ihrer Sicht-Modell, das Sie haben Kommentare, die besagt, "dass der Nutzer eine Warnung mit dem Fehler". Sie wollen nicht, um anzuzeigen, dass Fehler innerhalb der Funktion validiert. Stattdessen erstellen Sie eine Enumeration, die hat einen zugeordneten Wert (wo der Wert ist die Fehlermeldung, die Sie anzeigen möchten, um den Benutzer). Ändern Sie Ihre validate-Methode, so dass es den gibt, der enum. Dann in der view controller kann man bewerten, dass die Rückgabe-Wert und von dort wird die Warnung angezeigt, dialog. Denken Sie daran, die Sie nur benutzen wollen UIKit Verwandte Klassen nur innerhalb der view-controller - nie aus dem view-Modell. View-Modell sollten nur die Geschäftslogik enthalten.
2 - dies ist eine Frage der Einstellung und letztlich bestimmt durch die Anforderungen an Ihre app. In meiner Anwendung gebe ich diese Werte in der über die login () - Methode, D. H. login(username, Passwort).
3 - Erstellen Sie ein Protokoll namens LoginEventsDelegate und dann haben Sie eine Methode in es als solche:
Aber diese Methode sollte nur verwendet werden, informieren Sie den view-controller der tatsächlichen Ergebnisse von versuchen, die Anmeldung auf dem remote-server. Es sollte nichts zu tun haben mit der Validierung Teil. Ihre Validierungs-routine behandelt werden, wie besprochen, oben, in #1. Haben Sie Ihre view-controller implementieren, der LoginEventsDelegate. Und erstellen Sie eine öffentliche Eigenschaft auf Ihrem view-Modell, d.h.
Dann in den Abschluss-block für Ihre api-Aufruf können Sie teilen die Ansicht-controller über die Stellvertretung, d.h.
und Ihre view-controller würde dann so Aussehen:
Einige würden sagen, man kann nur eine Schließung, um die login-Methode und die skip-das Protokoll zusammen. Es gibt ein paar Gründe, warum ich denke, dass ist eine schlechte Idee.
Übergabe einer Schließung aus dem UI-Layer (UIL), um die Business Logik Layer (BLL) würde brechen, Separation of Concerns (SOC). Die Login () - Methode befindet sich in der BLL-also im Grunde genommen würden Sie sagen: "hey, BLL führen Sie diese UIL Logik für mich". Das ist ein SOC, Nein, Nein!
BLL sollten nur die Kommunikation mit der UIL über delegieren von Benachrichtigungen. So BLL ist im wesentlichen sagen, "Hey UIL, ich bin fertig ausgeführt meine Logik, und hier sind einige Daten, die Argumente, die Sie verwenden können, um die Manipulation der UI-Steuerelemente, wie Sie benötigen".
So UIL sollte nie Fragen, BLL zum ausführen von UI-Logik für ihn. Sollte nur Fragen BLL informiert ihn.
4 - ich habe gesehen, ReactiveCocoa und hörte gute Dinge über Sie, aber haben es nie benutzt. So kann nicht sprechen, es aus persönlicher Erfahrung. Ich würde sehen, wie mit einfachen Stellvertreter-Benachrichtigung (siehe #3) arbeitet für Sie in Ihrem Szenario. Wenn es erfüllt die brauchen dann toll, wenn Sie auf der Suche nach etwas ein wenig komplexer, dann vielleicht schauen Sie in ReactiveCocoa.
Btw, auch dies ist technisch nicht eine MVVM-Ansatz, da die Bindung und die Befehle werden nicht verwendet, aber das ist nur "ta-may-toe" | "ta-mah-toe" Erbsenzählerei IMHO. SOC Grundsätze sind alle gleich, unabhängig von der MV* Ansatz, den Sie verwenden.
var updateLoadingStatus: (()->())?
. Tut diese Pause SOC? Ist es besser, es zu vermeiden?MVVM in iOS bedeutet, dass ein Objekt mit Daten gefüllt, dass Ihr Bildschirm verwendet, getrennt von Ihren Model-Klassen. Es in der Regel Karten, die alle Elemente im UI, die konsumieren oder produzieren Daten, wie Beschriftungen, Textfelder, datasources oder dynamische Bilder. Es tut oft einige leichte überprüfung der Eingabe (leeres Feld, ist gültige E-Mail-oder keine positive Zahl, switch on ist oder nicht) mit Prüfungen. Diese Prüfungen sind in der Regel separate Klassen, die nicht inline-Logik.
Ihre View-Schicht weiß, über diese VM Klasse und beobachtet die änderungen reflektiert, die Ihnen und auch updates der VM-Klasse, wenn die Eingaben des Benutzers Daten. Alle Eigenschaften in der VM gebunden sind Elemente in der Benutzeroberfläche. So zum Beispiel ein Benutzer auf eine Benutzer-Registrierung-Bildschirm in diesem Bildschirm wird eine VM aus, die keine der Eigenschaften gefüllt, außer der status-Eigenschaft, die eine Unvollständige status. Die View weiß, dass nur ein Vollständiger form vorgelegt werden können, so setzt er den Submit-button inaktiv jetzt.
Dann der Benutzer füllt es in die details und macht einen Fehler in der e-mail-Adresse format. Die Prüfung für das Feld in der VM setzt nun ein Fehler-Zustand und die Ansicht setzt den Fehlerstatus (rot umrandet zum Beispiel) und die Fehlermeldung in der VM Prüfer in der UI.
Schließlich, wenn Sie alle erforderlichen Felder innerhalb der VM den status Abgeschlossen der VM Abgeschlossen ist, wird die View beobachtet das und setzt nun die Schaltfläche "Senden" aktiv, so kann der Benutzer Einreichen. Der Submit-button action verdrahtet ist, um die VC-und VC stellt sicher, dass die VM bekommt die Verknüpfung mit dem richtigen Modell(en) und gespeichert. Manchmal Modelle direkt verwendet werden, als ein VM, das könnte nützlich sein, wenn Sie einfacher CRUD-wie-Bildschirme.
Ich habe mit diesem Muster in WPF und es funktioniert wirklich toll. Es klingt wie eine Menge ärger einstellen diejenigen Beobachter in Aussicht und setzen eine Menge von Bereichen in Modell-Klassen als auch ViewModel-Klassen, aber ein guter MVVM-framework wird Ihnen dabei helfen. Sie müssen nur an-link UI-Elemente zu VM Elemente von der richtigen Art ist, das Recht abzutreten Prüfungen und eine Menge von dieser Sanitär wird für Sie erledigt, ohne die Notwendigkeit für das hinzufügen alle, die boilerplate-code selbst.
Einige Vorteile von diesem Muster:
boilerplate-code zu verbinden, UI-Elemente, um Daten
Nachteile: