简体   繁体   English

处理网络错误以及绑定到tableView(Moya,RxSwift,RxCocoa)

[英]Handling Network error in combination with binding to tableView (Moya, RxSwift, RxCocoa)

I am currently using Moya to make my network requests. 我目前正在使用Moya来发出我的网络请求。 I have implemented the following from one of the example projects @ https://github.com/DroidsOnRoids/RxSwiftExamples#tutorials 我已经从其中一个示例项目实现了以下内容@ https://github.com/DroidsOnRoids/RxSwiftExamples#tutorials

Below I have set up restaurantSearch so that when someone enters text it makes a new request. 下面我设置了restaurantSearch,这样当有人输入文本时,它会发出新的请求。

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

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

I am calling this method and binding it to a tableView like so: 我正在调用此方法并将其绑定到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)
}

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. 如果我关闭互联网并尝试更改邮政编码,则tableView保持原样。 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. 似乎它失去了它的约束力。

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 首先,我尝试在bindTo方法调用之前添加catchOnError,但我在评论中读到它不应该作为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([])
}

So I have 2 questions: 所以我有两个问题:

1) Where and how should I handle a network error? 1)我应该在何处以及如何处理网络错误?

2) Why does the tableView not update after I turn back on the internet? 2)为什么在我重新上网后tableView没有更新?

Any help much appreciated. 任何帮助非常感谢。

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). 这样做的好方法是将你的网络结果包装成某种枚举(类似于可选,但如果为n则有错误)。 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]>>

If you use MVVM architecture you would then filter only successful results in view model and provide that data to view controller. 如果使用MVVM体系结构,则只能在视图模型中过滤成功结果,并将该数据提供给视图控制器。

Also you should take a look at Driver unit. 你还应该看一下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. 它是专门为UI绑定制作的单元,因此它只在主线程上订阅,从不订阅错误并共享最后结果。

To translate Observable to Driver you use one of the three methods: 要将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>

This approach would automatically deal with your second question, because when Observable emits an error, all subscribers unsubscribe, ie. 这种方法会自动处理你的第二个问题,因为当Observable发出错误时,所有订阅者都会取消订阅,即。 it terminates the stream. 它终止了流。 Try to put .debug() behind your api.restaurants() call and see by yourself that it unsubscribes. 尝试将.debug()放在你的api.restaurants()调用后面,然后自己看看它是否取消订阅。

You can read more about Driver and other units here 您可以在此处阅读有关Driver和其他单元的更多信息

只需在绑定表之前添加catchErrorJustReturn([])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM