结合绑定到 tableView 处理网络错误(Moya、RxSwift、RxCocoa)
Handling Network error in combination with binding to tableView (Moya, RxSwift, RxCocoa)
我目前正在使用 Moya 进行网络请求。我已经从示例项目之一实现了以下内容 @ https://github.com/DroidsOnRoids/RxSwiftExamples#tutorials
下面我设置了 restaurantSearch,这样当有人输入文本时它会发出新请求。
var restaurantSearch: Observable<(String)> {
return searchBar
.rx_text
.throttle(0.5, scheduler: MainScheduler.instance)
.distinctUntilChanged()
}
我有一个方法 returns [餐厅]
类型的可观察对象
func restaurants() -> Observable<[Restaurant]> {
return restaurantSearch
.observeOn(MainScheduler.instance)
.flatMapLatest { postcode -> Observable<[Restaurant]?> in
return self.getRestaurants(postcode, cuisine: "", restaurantName: "")
}.replaceNilWith([])
}
internal func getRestaurants(postcode: String, cuisine: String, restaurantName: String) -> Observable<[Restaurant]?> {
return self.justEatProvider
.request(.Restaurant(postcode, cuisine, restaurantName))
.debug()
.mapArrayOptional(Restaurant.self, keyPath: "Restaurants")
}
我正在调用此方法并将其绑定到 tableView,如下所示:
func setupRx() {
api = JustEatApi(provider: provider, restaurantSearch: restaurantSearch)
api
.restaurants()
.bindTo(tableView.rx_itemsWithCellIdentifier("RestaurantTableViewCell", cellType: RestaurantTableViewCell.self)) { (row, element, cell) in
cell.restaurant = element
}
.addDisposableTo(disposeBag)
}
这工作正常。如果我输入邮政编码,它会进行搜索并填充 tableView。
如果我关闭互联网并尝试更改邮政编码,tableView 将保持原样。但是,当我滚动时,它使我的应用程序崩溃并显示以下内容:
@noreturn func rxFatalError(lastMessage: String) {
// The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours.
fatalError(lastMessage)
}
此外,如果我不滚动,只是返回互联网并更改邮政编码,则什么也不会发生。它似乎失去了它的约束力。
首先,我尝试在 bindTo
方法调用之前添加 catchOnError,但我在评论中看到它不应作为 UIBinding 的一部分处理:http://blog.scottlogic.com/2014/05/11/reactivecocoa-tableview-binding.html
我猜我应该在方法中处理它:
func restaurants() -> Observable<[Restaurant]> {
return restaurantSearch
.observeOn(MainScheduler.instance)
.flatMapLatest { postcode -> Observable<[Restaurant]?> in
return self.getRestaurants(postcode, cuisine: "", restaurantName: "")
}.replaceNilWith([])
}
所以我有两个问题:
1) 我应该在哪里以及如何处理网络错误?
2)为什么我重新上网后tableView没有更新?
非常感谢任何帮助。
我亲自处理 make/parse 网络请求的服务中的网络错误。这样做的好方法是将您的网络结果包装在某种枚举中(类似于可选,但如果为零则出错)。所以你会有这样的东西:
enum APIResult<T> {
case Success(T)
case Error(ErrorType)
}
然后你的服务会return像这样
Observable<APIResult<[Restaurant]>>
如果您使用 MVVM 架构,那么您将只在视图模型中过滤成功的结果,并将该数据提供给视图控制器。
另外你应该看看Driver单元。它是专门为 UI 绑定制作的单元,因此它仅在主线程上订阅,从不出错并共享最后的结果。
要将 Observable 转换为 Driver,您可以使用以下三种方法之一:
func asDriver(onErrorJustReturn onErrorJustReturn: Self.E) -> RxCocoa.Driver<Self.E>
func asDriver(onErrorDriveWith onErrorDriveWith: RxCocoa.Driver<Self.E>) -> RxCocoa.Driver<Self.E>
func asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> RxCocoa.Driver<Self.E>) -> RxCocoa.Driver<Self.E>
这种方法会自动处理你的第二个问题,因为当 Observable 发出错误时,所有订阅者都会取消订阅,即。它终止流。尝试将 .debug() 放在你的 api.restaurants() 调用后面,然后你自己看看它是否取消订阅。
您可以阅读有关 Driver 和其他单位的更多信息 here
只需在绑定 table.
之前添加 catchErrorJustReturn([])
我目前正在使用 Moya 进行网络请求。我已经从示例项目之一实现了以下内容 @ https://github.com/DroidsOnRoids/RxSwiftExamples#tutorials
下面我设置了 restaurantSearch,这样当有人输入文本时它会发出新请求。
var restaurantSearch: Observable<(String)> {
return searchBar
.rx_text
.throttle(0.5, scheduler: MainScheduler.instance)
.distinctUntilChanged()
}
我有一个方法 returns [餐厅]
类型的可观察对象func restaurants() -> Observable<[Restaurant]> {
return restaurantSearch
.observeOn(MainScheduler.instance)
.flatMapLatest { postcode -> Observable<[Restaurant]?> in
return self.getRestaurants(postcode, cuisine: "", restaurantName: "")
}.replaceNilWith([])
}
internal func getRestaurants(postcode: String, cuisine: String, restaurantName: String) -> Observable<[Restaurant]?> {
return self.justEatProvider
.request(.Restaurant(postcode, cuisine, restaurantName))
.debug()
.mapArrayOptional(Restaurant.self, keyPath: "Restaurants")
}
我正在调用此方法并将其绑定到 tableView,如下所示:
func setupRx() {
api = JustEatApi(provider: provider, restaurantSearch: restaurantSearch)
api
.restaurants()
.bindTo(tableView.rx_itemsWithCellIdentifier("RestaurantTableViewCell", cellType: RestaurantTableViewCell.self)) { (row, element, cell) in
cell.restaurant = element
}
.addDisposableTo(disposeBag)
}
这工作正常。如果我输入邮政编码,它会进行搜索并填充 tableView。
如果我关闭互联网并尝试更改邮政编码,tableView 将保持原样。但是,当我滚动时,它使我的应用程序崩溃并显示以下内容:
@noreturn func rxFatalError(lastMessage: String) {
// The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours.
fatalError(lastMessage)
}
此外,如果我不滚动,只是返回互联网并更改邮政编码,则什么也不会发生。它似乎失去了它的约束力。
首先,我尝试在 bindTo
方法调用之前添加 catchOnError,但我在评论中看到它不应作为 UIBinding 的一部分处理:http://blog.scottlogic.com/2014/05/11/reactivecocoa-tableview-binding.html
我猜我应该在方法中处理它:
func restaurants() -> Observable<[Restaurant]> {
return restaurantSearch
.observeOn(MainScheduler.instance)
.flatMapLatest { postcode -> Observable<[Restaurant]?> in
return self.getRestaurants(postcode, cuisine: "", restaurantName: "")
}.replaceNilWith([])
}
所以我有两个问题:
1) 我应该在哪里以及如何处理网络错误?
2)为什么我重新上网后tableView没有更新?
非常感谢任何帮助。
我亲自处理 make/parse 网络请求的服务中的网络错误。这样做的好方法是将您的网络结果包装在某种枚举中(类似于可选,但如果为零则出错)。所以你会有这样的东西:
enum APIResult<T> {
case Success(T)
case Error(ErrorType)
}
然后你的服务会return像这样
Observable<APIResult<[Restaurant]>>
如果您使用 MVVM 架构,那么您将只在视图模型中过滤成功的结果,并将该数据提供给视图控制器。
另外你应该看看Driver单元。它是专门为 UI 绑定制作的单元,因此它仅在主线程上订阅,从不出错并共享最后的结果。
要将 Observable 转换为 Driver,您可以使用以下三种方法之一:
func asDriver(onErrorJustReturn onErrorJustReturn: Self.E) -> RxCocoa.Driver<Self.E>
func asDriver(onErrorDriveWith onErrorDriveWith: RxCocoa.Driver<Self.E>) -> RxCocoa.Driver<Self.E>
func asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> RxCocoa.Driver<Self.E>) -> RxCocoa.Driver<Self.E>
这种方法会自动处理你的第二个问题,因为当 Observable 发出错误时,所有订阅者都会取消订阅,即。它终止流。尝试将 .debug() 放在你的 api.restaurants() 调用后面,然后你自己看看它是否取消订阅。
您可以阅读有关 Driver 和其他单位的更多信息 here
只需在绑定 table.
之前添加 catchErrorJustReturn([])