2016-03-08 36 views
8

Rozwijam aplikacje iOS, używając paradygmatu ViewModel Model View, aby uporządkować moje kontrolery widoku i reprezentować ich dane. To w połączeniu z ReactiveCocoa jest potężnym narzędziem; zobacz, jak kontrolery stają się mniej rozdęte, modele widoków są łatwiejsze do przetestowania i istnieje wyraźny rozdział obaw.Ogólna architektura sieciowa MVVM

Jedynym problemem, jaki mam z tą szczególną architekturą, jest to, że podobnie jak w przypadku MVC, wciąż nie ma jasnego miejsca ani sposobu na uporządkowanie kodu sieciowego. Weźmy następujący trywialny przykład:

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() 
     } 
    } 
} 

Wtedy moim zdaniem kontrolera gdzieś mogę zrobić:

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

Dla mnie wydaje się, jakby cały punkt przy użyciu MVVM było nie narażać poglądy i widok kontroler (co nazywam warstwą widoku prezentacji) do dowolnego kodu sieciowego, ale nadal potrzebuję sposobu, aby móc zaobserwować, że nowa treść została pobrana, nie znając szczegółów, tylko że udało się pobrać. Wyobrażam sobie, że będzie wyglądać następująco:

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

Jednocześnie, ja też nie chcę stracić zdolność do wiązania sygnały i producentów sygnałowe do zmiennych właściwości na mój korzystania <~.

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 
    } 
} 

Ta metoda robi to trochę lepiej, ale nadal korzysta z efektów ubocznych, aby wysłać kolejną imprezę na obserwatora sygnału i jeśli używasz wspólną klasę bazową ViewModel, trzeba odsłonić tak, że obserwator te podklasy mogą z niego korzystać.

Poszukuję wszelkich usprawnień, które można wprowadzić w architekturze MVVM, albo zmian w samej architekturze, tak aby nie była to już MVVM i ułatwiająca tworzenie sieci w lepszy i bardziej ogólny sposób, albo nawet rodzaj ogólnego protokołu bazowego do przeglądania modeli, które usuwają cały proces.

Kluczowe jest dla mnie bycie generycznym, jak to możliwe, przy jednoczesnym ujawnianiu jak najmniej informacji o modelu widoku. Idealnie chciałbym, aby każdy kontroler widoku wchodził w interakcję z modelem widoku w dokładnie taki sam sposób, jeśli chodzi o działanie sieci.

EDIT:

Per AT @ lonut sugestią, przeniosłem niektóre kodu sieciowego do moich klasach modelowych ale metodami tylko jako statyczne:

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 
    } 
} 

ten sposób modele mogą wdrożyć sieć połączeń w woli, która ma zaletę odejścia od szczegółów implementacji, takich jak konkretne połączenia sieciowe Moya, mapowanie obiektów odpowiedzi itp.

Wyobraź sobie, że masz model User:

User.get("myUserID") 

Nie rozwiązuje to całkowicie problemu, w jaki sposób kontroler widoku i model widoku powinny wchodzić ze sobą w interakcje, ale zdecydowanie przenosi kod sieci do pojedynczego punktu awarii.

Odpowiedz

1

Nie jestem zbyt zaawansowany w używaniu MVVM lub RAC, ale z tego, co grałem z kodem sieciowym, powinien być w części modelu, to jest metoda, która obserwuje wyniki w części modelu widoku (coś w stylu " fetchPosts() ") iw części widoku wywoływana jest metoda fetchPosts.Polecam ci this blog post po więcej informacji.

+0

Nie zgadzam się, ponieważ model powinien być tylko reprezentacją danych. Chociaż może działać, o ile metody pobierania danych są metodami klasy, a nie metodami instancji. – barndog

+0

Myślałem o modelu takim jak PostSearch, który ma kod sieciowy, a nie że model Post powinien również zawierać kod sieci. – Ionut