MVVM通用网络架构

bar*_*dog 9 architecture network-programming mvvm ios swift

我使用Model View ViewModel范例开发iOS应用程序来构建我的视图控制器并表示它们的数据.与ReactiveCocoa一起使用是一个强大的工具; 视图控制器变得不那么臃肿,视图模型更容易测试,并且关注点明显分离.

我对这个特定架构的一个问题是,像MVC一样,仍然没有一个明确的地方或方法来构建网络代码.采取以下简单的例子:

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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的视图控制器中的某个地方,我可以做:

self.viewModel.posts <~ self.viewModel.fetchPosts().on(next: { _ in self.collectionView.reloadData() })
Run Code Online (Sandbox Code Playgroud)

对我来说,感觉使用MVVM的重点是不要将视图和视图控制器(我称之为视图表示层)暴露给任何网络代码,但我仍然需要一种能够观察到新内容的方法.在不知道具体细节的情况下获取,只是发生了成功的获取.我想这会是这样的:

self.viewModel.contentUpdatedSignal.observeNext { _ in self.collectionView.reloadData() }
Run Code Online (Sandbox Code Playgroud)

与此同时,我也不想失去将信号和信号生成器绑定到我使用的可变属性的能力<~.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这样做的方法稍好一些,但它仍然使用副作用在信号观察器上发送下一个事件,如果你使用的是公共ViewModel基类,则必须公开该观察者,以便子类可以使用它.

我正在寻找可以对MVVM体系结构进行的任何改进,要么改变体系结构本身,以便它不再是MVVM,而是以更好,更通用的方式促进网络,甚至是视图模型的某种通用基础协议.将整个过程抽象化.

对我来说,尽可能通用,同时尽可能少地展示关于视图模型的信息是关键.理想情况下,我希望每个视图控制器与视图模型的交互方式与网络的工作方式完全相同.

编辑:

根据@ lonut的建议,我将一些网络代码移动到我的模型类中,但仅作为静态方法:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方式模型可以随意实现网络调用,这样可以抽象出实现细节,例如特定的Moya网络调用,响应对象的映射等.

想象一下你有一个User模特:

User.get("myUserID")
Run Code Online (Sandbox Code Playgroud)

它并没有完全解决视图控制器和视图模型应该如何相互交互的问题,但它肯定会将网络代码移动到单点故障.

小智 1

我在使用 MVVM 或 RAC 方面不是很先进,但从我所使用的网络代码来看,应该在模型部分,然后有一种方法可以观察视图模型部分的结果(类似于“fetchPosts()”) )并在视图部分调用 fetchPosts 方法。我向您推荐这篇博文以获取更多信息。