2016-03-08 23 views
8

Sviluppo applicazioni iOS utilizzando il paradigma Model View ViewModel per strutturare i miei controller di visualizzazione e per rappresentare i loro dati. Quello in congiunzione con ReactiveCocoa è un potente strumento; i controller di visualizzazione diventano meno gonfiati, i modelli di visualizzazione sono più facili da testare e c'è una netta separazione delle preoccupazioni.Architettura di rete generica MVVM

L'unico problema che ho con questa particolare architettura è che, come MVC, non c'è ancora un luogo o un modo chiaro per strutturare il codice di rete. Prendiamo il seguente esempio banale:

class HomepageViewModel { 
    var posts: MutableProperty<[Post]> = MutableProperty([]) 

    func fetchPosts() -> SignalProducer<[Post], NSError> { 
     return SignalProducer { observer, disposable in 
      // do some networking stuff 
      let posts = .... 
      observer.sendNext(posts) 
      observer.sendCompleted() 
     } 
    } 
} 

Poi a mio controller della vista da qualche parte che posso fare:

self.viewModel.posts <~ self.viewModel.fetchPosts().on(next: { _ in self.collectionView.reloadData() }) 

Per me ci si sente come il punto di tutta usando MVVM è stato quello di non esporre i punti di vista e la vista controller (quello che chiamo il livello di presentazione della vista) a qualsiasi codice di rete, ma ho ancora bisogno di un modo per essere in grado di osservare che il nuovo contenuto è stato recuperato senza conoscere le specifiche, solo che si è verificato un recupero riuscito. Mi immagino che sarebbe un aspetto simile:

self.viewModel.contentUpdatedSignal.observeNext { _ in self.collectionView.reloadData() } 

Allo stesso tempo, anche io non voglio perdere la capacità di legare i segnali e produttori di segnale per immobili mutevoli sul mio usando <~.

class ViewModel { 
    let someProperty = MutableProperty<[SomeModel]>([]) 
    var (contentUpdatedSignal, observer) = Signal.pipe() 
    init() { 
     self.someProperty <~ self.fetchContent().on(next: { _ in observer.sendNext() } 
    } 

    func fetchContent() -> SignalProducer<[SomeModel], NSError> { 
     // do some fun stuff 
    } 
} 

Questo metodo di fare questo è un po 'meglio, ma usa ancora gli effetti collaterali di inviare un prossimo evento sull'osservatore del segnale e se si sta utilizzando una classe ViewModel base comune, si deve esporre che all'osservatore così che le sottoclassi possono usarlo.

Sono alla ricerca di eventuali miglioramenti che possono essere apportati all'architettura MVVM, modifiche all'architettura stessa in modo che non sia più MVVM e faciliti il ​​networking in un modo migliore e più generico o persino una sorta di protocollo di base generico per i modelli di vista che astrae l'intero processo.

Per me, essere il più generico possibile esponendo il meno possibile le informazioni sul modello di visualizzazione è la chiave. Idealmente, mi piacerebbe che ogni controller di visualizzazione interagisse con il modello di visualizzazione esattamente nello stesso modo in relazione al funzionamento della rete.

EDIT:

Per a @ di lonut suggerimento, mi sono trasferito parte del codice di rete ai miei classi del modello, ma solo metodi statici:

import Foundation 
import ReactiveCocoa 
import Moya 

protocol RESTModel { 
    typealias Model 

    static func create(parameters: [NSObject: AnyObject]?) -> SignalProducer<Model, Moya.Error> 
    static func find() -> SignalProducer<Model, Moya.Error> 
    static func get(id: String) -> SignalProducer<Model, Moya.Error> 
    static func update(id: String) -> SignalProducer<Model, Moya.Error> 
    static func remove(id: String) -> SignalProducer<Model, Moya.Error> 

} 

extension RESTModel { 
    static func create(parameters: [NSObject: AnyObject]? = nil) -> SignalProducer<Self.Model, Moya.Error> { 
     return SignalProducer.empty 
    } 
    static func find() -> SignalProducer<Self.Model, Moya.Error> { 
     return SignalProducer.empty 
    } 
    static func get(id: String) -> SignalProducer<Self.Model, Moya.Error> { 
     return SignalProducer.empty 
    } 
    static func update(id: String) -> SignalProducer<Self.Model, Moya.Error> { 
     return SignalProducer.empty 
    } 
    static func remove(id: String) -> SignalProducer<Self.Model, Moya.Error> { 
     return SignalProducer.empty 
    } 
} 

In questo modo i modelli possono implementare rete agli scali in volontà che ha il vantaggio di sradicare i dettagli dell'implementazione come specifiche chiamate di rete Moya, mappatura di oggetti risposta, ecc.

Immagina di avere un modello User:

User.get("myUserID") 

Non risolve completamente il problema di come il controller di visualizzazione e il modello di visualizzazione devono interagire tra loro, ma sposta definitivamente il codice di rete in un singolo punto di errore.

risposta

1

Non sono molto avanzato nell'utilizzo di MVVM o RAC, ma da quello che ho giocato con il codice di rete dovrebbe essere nella parte del modello, quindi c'è un metodo che osserva i risultati nella parte del modello di vista (qualcosa del tipo " fetchPosts() ") e nella parte vista viene chiamato il metodo fetchPosts.Ti consiglio di aggiungere this blog post per molte altre informazioni.

+0

Non sarei d'accordo, dal momento che il modello dovrebbe essere solo una rappresentazione di dati. Sebbene possa funzionare, purché i metodi di recupero dei dati siano metodi di classe, non metodi di istanza. – barndog

+0

Stavo pensando a un modello come PostSearch che ha il codice di rete, non che il modello Post dovrebbe contenere anche il codice di rete. – Ionut

Problemi correlati