Utilizzo di MVVM in iOS

4

Sono uno sviluppatore iOS e sono colpevole di avere Massive View Controller nei miei progetti, quindi ho cercato un modo migliore per strutturare i miei progetti e ho trovato l'architettura MVVM (Model-View-ViewModel). Ho letto molto MVVM con iOS e ho un paio di domande. Spiegherò i miei problemi con un esempio.

Ho un controller di visualizzazione chiamato 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()
    }
}

Non ha una classe Model. Ma ho creato un modello di visualizzazione chiamato LoginViewModel per inserire la logica di convalida e le chiamate di rete.

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
        }
    }
}
  1. La mia prima domanda è semplicemente che la mia implementazione MVVM sia corretta? Ho questo dubbio perché ad esempio inserisco l'evento di tocco del pulsante di accesso ( loginButtonPressed ) nel controller. Non ho creato una vista separata per la schermata di accesso perché ha solo un paio di campi di testo e un pulsante. È accettabile che il controller abbia metodi evento legati agli elementi dell'interfaccia utente?

  2. La mia prossima domanda riguarda anche il pulsante di accesso. Quando l'utente tocca il pulsante, i valori del nome utente e della password devono essere passati a LoginViewModel per la convalida e, in caso di esito positivo, alla chiamata API. La mia domanda su come passare i valori al modello di vista. Dovrei aggiungere due parametri al metodo login() e passarli quando li chiamo dal controller di visualizzazione? O dovrei dichiarare le proprietà per loro nel modello di vista e impostare i loro valori dal controller di visualizzazione? Quale è accettabile in MVVM?

  3. Utilizza il metodo validate() nel modello di visualizzazione. L'utente dovrebbe essere avvisato se uno di questi è vuoto. Ciò significa che dopo il controllo, il risultato deve essere restituito al controller della vista per intraprendere le azioni necessarie (mostrare un avviso). Stessa cosa con il metodo login() . Avvisa l'utente se la richiesta fallisce o passa al controller della vista successivo se ha successo. Come posso notificare il controller di questi eventi dal modello di visualizzazione? È possibile utilizzare meccanismi di binding come KVO in casi come questo?

  4. Quali sono gli altri meccanismi di associazione quando si utilizza MVVM per iOS? KVO è uno. Ma ho letto che non è adatto per i progetti più grandi perché richiede un sacco di codice boilerplate (registrando / annullando la registrazione degli osservatori, ecc.). Quali sono le altre opzioni? So che ReactiveCocoa è un framework usato per questo, ma sto cercando di vedere se ci sono altri nativi.

Tutti i materiali che ho trovato su MVVM su Internet hanno fornito poche informazioni su queste parti che sto cercando di chiarire, quindi apprezzerei molto le tue risposte.

    
posta Isuru 29.11.2014 - 13:30
fonte

1 risposta

6

Mi sto solo avvicinando a queste domande, ma darò le risposte migliori che posso e fare alcune osservazioni.

Ci viene detto che MVC di iOS ci aspetta che interrompiamo il nostro programma in modelli, viste e controllori che collegano specifiche visualizzazioni a modelli specifici. Purtroppo, in pratica avremo un numero di visualizzazioni e un certo numero di modelli, ma solo un controller (View) si prenderà cura di tutti i collegamenti. Questo è ciò che porta a massivi ViewControllers.

L'adozione di MVVM in questo contesto significa rompere il ViewController in due parti. La parte che tratta direttamente con Views (che rimane nella classe ViewController corretta) e la parte che tratta direttamente con Models (che viene spostata nella classe / struct ModelView).

Questo significa che solo il ViewController dovrebbe inviare / ricevere messaggi da / a Views e l'unico oggetto non vista con cui VC deve conversare è ModelView. Mentre ModelView dovrebbe conversare con tutti i vari oggetti del modello e non dovrebbe mai conversare con una vista.

La nozione precedente di ViewController / ViewModel suddivide leeds alle seguenti risposte alle tue domande:

  1. No, l'implementazione MVVM non è corretta. I tuoi metodi ViewModel hanno un difetto fondamentale. Non dovrebbero parlare direttamente con alcun oggetto vista, quindi non hanno il compito di mostrare all'utente alcun avviso. Questo è il lavoro di ViewController. ViewModel non è inoltre responsabile della transizione tra ViewControllers.

  2. Il ViewController non dovrebbe occuparsi di alcun oggetto del modello. Dovrebbe conversare solo con Views e ModelView. Pertanto, il ViewController dovrebbe non estrarre il nome utente e la password dalle sue viste e passarle alla funzione di login di ViewModel. Ciò richiederebbe al ViewController di capire qualcosa sulle differenze tra i nomi utente e le password e quando è opportuno utilizzarle.

Invece, ViewController dovrebbe informare ViewModel quando si verificano cambiamenti nei campi username e password. Dovrebbe informare ViewModel quando viene premuto il pulsante di accesso. Non dovrebbe avere alcuna conoscenza su quali oggetti modello ha bisogno di viewModel in un particolare metodo.

  1. Mi aspetto che la funzione validate() restituisca un Bool (meglio chiamarlo isValid e ne faccia una proprietà calcolata). Prevedo che la funzione login() accetti due blocchi (uno per il passaggio e uno per fail.) Può comunicare ciò che è accaduto chiamando il blocco corretto. Un'altra opzione sarebbe quella di avere la funzione accetta un blocco come parametro con un success booleano. Quindi qualcosa di simile:

-

struct LoginViewModel {

    var username: String = ""
    var password: String = ""

    init(username: String, password: String) {
        self.username = username
        self.password = password
    }

    var isValid: Bool {
        return !username.isEmpty && !password.isEmpty
    }

    func login(callback: (error: NSError?) -> Void) {
        let api = ApiHandler()
        api.login(username, password: password, success: { (data) -> Void in
            callback(error: nil)
            }) { (error) -> Void in
                callback(error: error)
        }
    }

}
  1. Nel paradigma MVVM, ReactiveCocoa (e RxSwift è un'altra libreria) vengono utilizzati per comunicare le modifiche in ModelView al ViewController, ma lascia che ti chieda questo ... Il tuo ModelView cambia in qualsiasi modo sostanziale con il tuo ViewController non lo sa? Finora, non sono stato in una situazione in cui qualsiasi altro oggetto sta inviando messaggi al mio ModelView oltre al ViewController a cui è collegato, quindi non c'è alcun modo per il ModelView di mutare senza ViewController sapendo che è almeno possibile. Per questo motivo, generalmente ho una funzione updateUI() nel mio ViewController. Ogni volta che ViewController invia un messaggio potenzialmente mutante a ModelView, chiama quindi updateUI() che esamina ViewModel e aggiorna l'interfaccia utente di conseguenza. Non c'è bisogno di trucchi.

- Spero che questo ti aiuti e spero che la mia risposta stimolerà qualche discussione su questa domanda.

    
risposta data 13.06.2015 - 21:30
fonte

Leggi altre domande sui tag