【发布时间】:2018-05-02 10:51:01
【问题描述】:
我知道状态是响应式编程的敌人,但我在学习 RxSwift 的过程中正在处理它。
我的应用程序非常简单,第一个屏幕是一个列表和书籍搜索,第二个屏幕是书籍的详细信息,您可以在其中添加/删除一本书到您的书架并标记它作为已读/未读。
为了显示本书的详细信息,我创建了一个BookViewModel,传递了一个BooksService 来执行网络操作并显示当前的Book。
问题是我必须跟踪书中的更改才能更改 UI:例如,在删除书籍后,之前显示“删除”的按钮现在必须显示“添加”。
我使用作为Driver<Book> 向观察者公开的Variable<Book> 来实现此行为,但是当网络操作返回时我搞砸了很多,我必须按顺序更新Variable<Book> 的值触发 UI 的更新。
这是视图模型的初始化器:
init(book: Book, booksService: BooksService) {
self._book = Variable(book)
self.booksService = booksService
}
这是我公开的可观察对象
var book: Driver<Book> {
return _book.asDriver()
}
这是我添加/删除书籍的功能:
func set(toggleInShelfTrigger: Observable<Void>) {
toggleInShelfTrigger // An observable from a UIBarButtonItem tap
.map({ self._book.value }) // I have to map the variable's value to the actual book
.flatMap({ [booksService] book -> Observable<Book> in
return (book.isInShelf ?
booksService.delete(book: book) :
booksService.add(book: book))
}) // Here I have to know if the books is in the shelf or not in order to perform one operation or another.
.subscribe(onNext: { self._book.value = $0 }) // I have to update the variable's value in order to trigger the UI update
.disposed(by: disposeBag)
}
我对这段代码和整个视图模型非常不满意。它可以工作,但很笨重,而且本质上是错误的,因为如果网络操作失败,订阅将被丢弃,我的按钮将变得无响应。
如果我摆脱Variable<Book> 并从set(toggleInShelfTrigger: Observable<Void>) 方法返回Driver<Book>,我就不会遇到这种混乱,但我将无法知道是否必须添加或删除这本书。
那么,真实世界在这种应用程序中跟踪对象状态的方法是什么?如何仅使用 Rx 运算符来实现这一点?
编辑
我已经设法清理了那些糟糕的代码,但我仍在尝试在没有 Variable 和使用 scan 运算符的情况下实现状态。
这是我的新 BookViewModel 初始化器:
init(book: Book, booksService: BooksService) {
self.bookVariable = Variable(book)
let addResult = addBook
.mapBookFrom(bookVariable)
.flatMapLatest({ booksService.add(book: $0) })
.updateBookVariable(bookVariable)
let removeResult = ... // Similar to addResult, changing service call
let markReadResult = ... // Similar to addResult, changing service call
let markUnreadResult = ... // Similar to addResult, changing service call
self.book = Observable.of(addResult, removeResult, markReadResult, markUnreadResult).merge()
.startWith(.success(book))
}
我制作了几个自定义运算符来帮助我管理Variable<Book>,一个得到真正的Book:
private extension ObservableType where E == Void {
func mapBookFrom(_ variable: Variable<Book>) -> Observable<Book> {
return map({ _ in return variable.value })
}
}
另一个在服务返回后更新Variable:
private extension ObservableType where E == BookResult<Book> {
func updateBookVariable(_ variable: Variable<Book>) -> Observable<BookResult<Book>> {
return self.do(onNext: { result in
if case let .success(book) = result {
variable.value = book
}
})
}
}
现在我有一个非常干净的视图模型,但并不“完美”。
【问题讨论】:
标签: ios mvvm reactive-programming rx-swift