我使用模型-视图-视图模型范例来开发iOS应用程序,以构建我的视图控制器并表示它们的数据。结合ReactiveCocoa使用是一个强大的工具;视图控制器变得更加简洁,视图模型更易于测试,并且有明确的责任分离。
然而,我在这种特定架构中遇到的一个问题是,就像MVC一样,仍然没有明确的位置或方法来构建网络代码。 以以下微不足道的示例为例:
然后在我的视图控制器的某个地方,我可以这样做:
对我来说,使用MVVM的重点似乎是不将视图和视图控制器(我称之为视图呈现层)暴露给任何网络代码,但我仍然需要一种方法来观察是否获取了新内容,而不知道具体细节,只知道成功获取了。 我想这看起来应该是这样的:
这种方法略微改进了一下,但仍然使用副作用在信号观察器上发送下一个事件,如果您正在使用通用的
我正在寻找对MVVM架构进行任何改进的方法,无论是对架构本身进行更改,使其不再是MVVM并以更好、更通用的方式促进网络或者甚至是某种抽象了整个过程的通用基础协议来处理视图模型。
对我而言,尽可能通用且尽可能少地暴露有关视图模型的信息非常重要。理想情况下,我希望每个视图控制器与视图模型交互的方式在网络工作方面都完全相同。
编辑:
根据@lonut的建议,我将一些网络代码移动到我的模型类中,但仅作为静态方法:
这样,模型可以随意实现网络调用,从而抽象化实现细节,例如特定的Moya网络调用、响应对象的映射等。
想象一下你有一个“用户”模型:
然而,我在这种特定架构中遇到的一个问题是,就像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()
}
}
}
然后在我的视图控制器的某个地方,我可以这样做:
self.viewModel.posts <~ self.viewModel.fetchPosts().on(next: { _ in self.collectionView.reloadData() })
对我来说,使用MVVM的重点似乎是不将视图和视图控制器(我称之为视图呈现层)暴露给任何网络代码,但我仍然需要一种方法来观察是否获取了新内容,而不知道具体细节,只知道成功获取了。 我想这看起来应该是这样的:
self.viewModel.contentUpdatedSignal.observeNext { _ in self.collectionView.reloadData() }
同时,我也不想失去在使用 <~
时将信号和信号生成器绑定到可变属性的能力。
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
}
}
这种方法略微改进了一下,但仍然使用副作用在信号观察器上发送下一个事件,如果您正在使用通用的
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
}
}
这样,模型可以随意实现网络调用,从而抽象化实现细节,例如特定的Moya网络调用、响应对象的映射等。
想象一下你有一个“用户”模型:
User.get("myUserID")
它并没有完全解决视图控制器和视图模型之间如何交互的问题,但它确实将网络代码移动到了一个单一故障点。