2016-04-28 8 views
6

Ich verwende derzeit Moya, um meine Netzwerkanforderungen zu stellen. Ich habe folgendes aus einem der Beispielprojekte implementiert: @https://github.com/DroidsOnRoids/RxSwiftExamples#tutorialsUmgang mit Netzwerkfehler in Verbindung mit Bindung an TableView (Moya, RxSwift, RxCocoa)

Unten habe ich restaurantSearch eingerichtet, so dass, wenn jemand Text eingibt, es eine neue Anfrage stellt.

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

Ich habe eine Methode, die eine beobachtbare des Typs zurückgibt [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") 
} 

ich diese Methode nenne und es zu einem Tableview Bindung etwa 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) 
} 

Dies ist funktioniert gut. Wenn ich eine Postleitzahl eingabe, wird die Suche durchgeführt und die TabelleView wird ausgefüllt.

Wenn ich das Internet ausschalte und versuche, die Postleitzahl zu ändern, bleibt das TableView wie es ist. Allerdings, wenn ich scrollen stürzt meine Anwendung mit dem folgenden:

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

Auch wenn ich nicht scrollen, sondern nur auf das Internet zurückdrehen und ändern die Postleitzahl nichts passiert. Es scheint, als ob es seine Bindung verloren hat.

Zuerst habe ich versuchte catchOnError vor dem bindTo Methodenaufruf aber ich lese hier in Kommentar hinzu, dass es nicht als Teil des UIBinding behandelt werden soll: http://blog.scottlogic.com/2014/05/11/reactivecocoa-tableview-binding.html

Ich vermute, ich es in der Methode behandeln soll:

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

So habe ich 2 Fragen:

1) Wo und wie soll ich einen Netzwerkfehler umgehen?

2) Warum wird das TableView nicht aktualisiert, nachdem ich das Internet wieder eingeschaltet habe?

Jede Hilfe sehr geschätzt.

Antwort

8

Ich persönlich handle Netzwerkfehler in Diensten, die Netzwerkanforderungen erstellen/analysieren. Eine gute Möglichkeit wäre, Ihre Netzwerkergebnisse in eine Art Enum zu verpacken (ähnlich wie optional, aber mit einem Fehler, wenn nil). Also würden Sie so etwas wie:

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

Und dann Ihre Dienste so etwas wie diese

Observable<APIResult<[Restaurant]>> 

zurückkehren Wenn Sie MVVM Architektur verwenden würden Sie dann nur erfolgreiche Ergebnisse im Hinblick auf Modell auswählen, und diese Daten zur Verfügung stellen zu Controller anzeigen.

Auch sollten Sie sich Treiber Gerät ansehen. Es ist eine Einheit, die speziell für UI-Bindungen erstellt wurde, so dass sie nur Main-Thread abonniert, niemals Fehler und Shares Leisten-Ergebnis.

Um Sie beobachtbar Treiber zu übersetzen eine der drei Methoden:

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> 

Dieser Ansatz würde automatisch mit Ihrer zweiten Frage befassen, denn wenn beobachtbare einen Fehler sendet, alle Teilnehmer abmelden, dh. Es beendet den Stream. Versuchen Sie, .debug() hinter Ihren api.restaurants() Anruf und sehen Sie selbst, dass es sich abmeldet.

Sie können lesen Sie mehr über Treiber und andere Einheiten here

+0

Nice one! Persönlich, wenn ich Ansichtsmodelle verwende, habe ich auch diese Variable im Ansichtsmodell, die über Fehler informiert, so dass der Controller sie als eine unabhängige Einheit anhören kann. Abgesehen davon ist das 'APIResult' toll, Sie können dafür auch [Result] (https://github.com/antitypical/Result) verwenden. – sunshinejr