Suche…


MVVM ohne reaktive Programmierung

Ich fange mit einer wirklich kurzen Erklärung an, was ist und warum das Model-View-ViewModel (MVVM) -Designmuster in Ihren iOS-Apps verwendet wird. Als iOS zum ersten Mal auf den Markt kam, schlug Apple vor, MVC (Model-View-Controller) als Entwurfsmuster zu verwenden. Sie haben es in all ihren Beispielen gezeigt und alle ersten Entwickler waren glücklich damit, weil sie die Anliegen zwischen Geschäftslogik und Benutzeroberfläche gut voneinander trennten. Als Anwendungen immer umfangreicher und komplexer wurden, erschien ein neues Problem, das als Massive View Controller (MVC) bezeichnet wurde. Da die gesamte Geschäftslogik im ViewController hinzugefügt wurde, wurden sie mit der Zeit zu umfangreich und komplex. Um MVC-Probleme zu vermeiden, wurde ein neues Designmuster in die Welt des iOS eingeführt - das Model-View-ViewModel (MVVM) -Muster.

MVVM-Diagramm

Das Diagramm oben zeigt, wie MVVM aussieht. Sie haben einen Standard-ViewController + View (in Storyboard, XIB oder Code), der als MVVM-View fungiert (in späterem Text - View verweist auf MVVM-View). Eine Ansicht hat einen Verweis auf ein ViewModel, in dem sich unsere Geschäftslogik befindet. Es ist wichtig zu wissen, dass ViewModel nichts über die Ansicht weiß und nie einen Verweis auf die Ansicht hat. ViewModel hat eine Referenz auf ein Modell.
Dies reicht bei einem theoretischen Teil der MVVM aus. Mehr über sie gelesen werden kann hier .

Eines der Hauptprobleme bei MVVM ist das Aktualisieren von View über das ViewModel, wenn ViewModel keine Referenzen hat und nicht einmal etwas über die View weiß.

Der Hauptteil dieses Beispiels soll zeigen, wie MVVM (genauer gesagt, wie ViewModel und View gebunden werden) ohne reaktive Programmierung (ReactiveCocoa, ReactiveSwift oder RxSwif) verwendet werden. Nur eine Anmerkung: Wenn Sie die reaktive Programmierung verwenden möchten, ist die Verwendung von MVVM-Bindungen noch einfacher. In diesem Beispiel wird jedoch beschrieben, wie MVVM ohne Reactive-Programmierung verwendet wird.

Lassen Sie uns ein einfaches Beispiel erstellen, um die Verwendung von MVVM zu veranschaulichen.

Unser MVVMExampleViewController ist ein einfacher ViewController mit einem Label und einem Button. Wenn die Taste gedrückt wird, sollte der Etikettentext auf "Hallo" gesetzt werden. Da die Entscheidung, was bei Benutzerbenutzerinteraktionen zu tun ist, Teil der Geschäftslogik ist, muss ViewModel entscheiden, was zu tun ist, wenn der Benutzer die Schaltfläche drückt. MVVMs View sollte keine Geschäftslogik ausführen.

class MVVMExampleViewController: UIViewController {
    
    @IBOutlet weak var helloLabel: UILabel!
    
    var viewModel: MVVMExampleViewModel?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func sayHelloButtonPressed(_ sender: UIButton) {
        viewModel?.userTriggeredSayHelloButton()
    }
}

MVVMExampleViewModel ist ein einfaches ViewModel.

class MVVMExampleViewModel {
    
    func userTriggeredSayHelloButton() {
        // How to update View's label when there is no reference to the View??
    }
}

Sie fragen sich vielleicht, wie Sie die ViewModel-Referenz in der Ansicht einstellen. Normalerweise mache ich es, wenn ViewController initialisiert wird oder bevor er angezeigt wird. Für dieses einfache Beispiel würde ich in AppDelegate so etwas AppDelegate :

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        if let rootVC = window?.rootViewController as? MVVMExampleViewController {
            let viewModel = MVVMExampleViewModel()
            rootVC.viewModel = viewModel
        }
        
        return true

Die eigentliche Frage lautet nun: Wie kann ich View von ViewModel aktualisieren, ohne einen Hinweis auf die View auf das ViewModel zu geben? (Denken Sie daran, dass wir keine der reaktiven Programmier-iOS-Bibliotheken verwenden werden.)

Sie könnten über die Verwendung von KVO nachdenken, aber das würde die Dinge zu kompliziert machen. Einige kluge Leute haben über das Thema nachgedacht und die Bond-Bibliothek entworfen . Die Bibliothek mag auf den ersten Blick kompliziert und ein wenig schwieriger zu verstehen sein, also nehme ich nur einen kleinen Teil davon und mache unsere MVVM voll funktionsfähig.

Wir stellen die Dynamic Klasse vor, die den Kern unseres einfachen, aber voll funktionsfähigen MVVM-Musters bildet.

class Dynamic<T> {
    typealias Listener = (T) -> Void
    var listener: Listener?
    
    func bind(_ listener: Listener?) {
        self.listener = listener
    }
    
    func bindAndFire(_ listener: Listener?) {
        self.listener = listener
        listener?(value)
    }
    
    var value: T {
        didSet {
            listener?(value)
        }
    }
    
    init(_ v: T) {
        value = v
    }
}

Dynamic Klasse verwendet Generics und Closures, um unser ViewModel mit unserem View zu verbinden. Ich werde nicht auf Details zu dieser Klasse eingehen, wir können es in den Kommentaren machen (um dieses Beispiel zu verkürzen). Lassen Sie uns nun unseren MVVMExampleViewController und MVVMExampleViewModel , um diese Klassen zu verwenden.

Unser aktualisierter MVVMExampleViewController

class MVVMExampleViewController: UIViewController {
    
    @IBOutlet weak var helloLabel: UILabel!
    
    var viewModel: MVVMExampleViewModel?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        bindViewModel()
    }
    
    func bindViewModel() {
        if let viewModel = viewModel {
            viewModel.helloText.bind({ (helloText) in
                DispatchQueue.main.async {
                    // When value of the helloText Dynamic variable
                    // is set or changed in the ViewModel, this code will
                    // be executed
                    self.helloLabel.text = helloText
                }
            })
        }
    }
    
    @IBAction func sayHelloButtonPressed(_ sender: UIButton) {
        viewModel?.userTriggeredSayHelloButton()
    }
}

MVVMExampleViewModel aktualisiert:

    class MVVMExampleViewModel {
    
    // we have to initialize the Dynamic var with the
    // data type we want
    var helloText = Dynamic("")
    
    func userTriggeredSayHelloButton() {
        // Setting the value of the Dynamic variable
        // will trigger the closure we defined in the View
        helloText.value = "Hello"
    }
}

Das ist es. Ihr ViewModel jetzt View aktualisieren, ohne dass es einen Verweis auf die View .

Dies ist ein wirklich einfaches Beispiel, aber ich denke, Sie haben eine Vorstellung davon, wie mächtig dies sein kann. Ich werde nicht näher auf die Vorteile der MVVM eingehen, aber wenn Sie von MVCM zu MVVM wechseln, werden Sie nicht mehr zurückkehren. Probieren Sie es einfach aus und überzeugen Sie sich selbst.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow