与绑定到tableView结合处理网络错误(Moya,RxSwift,RxCocoa) [英] Handling Network error in combination with binding to tableView (Moya, RxSwift, RxCocoa)

查看:299
本文介绍了与绑定到tableView结合处理网络错误(Moya,RxSwift,RxCocoa)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我当前正在使用Moya发出网络请求.我已经从一个示例项目@ https://github.com/DroidsOnRoids/RxSwiftExamples#tutorials中实现了以下内容

I am currently using Moya to make my network requests. I have implemented the following from one of the example projects @ https://github.com/DroidsOnRoids/RxSwiftExamples#tutorials

在下面,我设置了restaurantSearch,以便当有人输入文本时发出新的请求.

Below I have set up restaurantSearch so that when someone enters text it makes a new request.

var restaurantSearch: Observable<(String)> {
    return searchBar
        .rx_text
        .throttle(0.5, scheduler: MainScheduler.instance)
        .distinctUntilChanged()
}

我有一个返回[Restaurant]类型的可观察对象的方法

I have a method that returns an observable of type [Restaurant]

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上,如下所示:

I am calling this method and binding it to a tableView like so:

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.

This is working fine. If I enter a postcode it does the search and the tableView is populated.

如果我关闭互联网并尝试更改邮政编码,则tableView保持原样.但是,当我滚动时,它使我的应用程序崩溃,并显示以下内容:

If I turn off the internet and try and change the postcode the tableView remains as is. However when I scroll it crashes my app with the following:

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

如果我不滚动,而是转回互联网并更改邮政编码,则什么也不会发生.似乎它已经失去了约束力.

Also if I don't scroll but instead just turn back on the internet and change the postcode nothing happens. It seems like it has lost it's binding.

首先,我尝试在bindTo方法调用之前添加catchOnError,但是我在此处阅读评论,认为它不应作为UIBinding的一部分进行处理:

Firstly I tried adding catchOnError before the bindTo method call but I read here in comment that it shouldn't be handled as part of UIBinding: http://blog.scottlogic.com/2014/05/11/reactivecocoa-tableview-binding.html

我猜我应该在方法中处理它:

I am guessing I should handle it in the method:

func restaurants() -> Observable<[Restaurant]> {
    return restaurantSearch
        .observeOn(MainScheduler.instance)
        .flatMapLatest { postcode -> Observable<[Restaurant]?> in
            return self.getRestaurants(postcode, cuisine: "", restaurantName: "")
        }.replaceNilWith([])
}

所以我有2个问题:

1)我应该在哪里以及如何处理网络错误?

1) Where and how should I handle a network error?

2)为什么重新打开互联网后,tableView为什么不更新?

2) Why does the tableView not update after I turn back on the internet?

非常感谢您的帮助.

推荐答案

我亲自处理发出/解析网络请求的服务中的网络错误.做到这一点的好方法是将您的网络结果包装在某种枚举中(类似于可选的枚举,但是如果为nil则报错).所以你会有类似的东西:

I personally handle network errors in services that make/parse network requests. Good way to do that would be to wrap your network results in some kind of enum (similar to optional, but with error if nil). So you would have something like:

enum APIResult<T> {
    case Success(T)
    case Error(ErrorType)
}

然后您的服务将返回类似的内容

And then your services would return something like this

Observable<APIResult<[Restaurant]>>

如果您使用MVVM体系结构,则仅在视图模型中过滤成功的结果,并将该数据提供给视图控制器.

If you use MVVM architecture you would then filter only successful results in view model and provide that data to view controller.

此外,您还应该查看 Driver 单元.它是专门用于UI绑定的单元,因此它仅在主线程上订阅,绝不会出错并共享最后的结果.

Also you should take a look at Driver unit. It is unit that is specifically made for UI bindings, so it subscribes on Main thread only, never errors and shares last result.

要将Observable转换为Driver,请使用以下三种方法之一:

To translate Observable to Driver you use one of the three methods:

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()调用后面,并亲自查看它是否已取消订阅.

This approach would automatically deal with your second question, because when Observable emits an error, all subscribers unsubscribe, ie. it terminates the stream. Try to put .debug() behind your api.restaurants() call and see by yourself that it unsubscribes.

您可以在此处

这篇关于与绑定到tableView结合处理网络错误(Moya,RxSwift,RxCocoa)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆