2017-04-25 1 views
4

Ich versuche, Best Practice zu finden, um mehrere Unteranfragen für jeden Wert von Eltern Anfrage zu behandeln. Ich versuche, die gleiche Logik wie hier zu verwenden - Reactive Cocoa 5 and ReactiveSwift network requests handling, aber habe ein paar Probleme.ReactiveCocoa 5, ReactiveSwift Netzwerk-Sub-Anfrage Handhabung und Best Practice

Was wir haben, und brauchen:
1. Tableview mit unendlicher Scrolling-Handler (SVPullToRefresh)
2. Fetch Liste von Objekten, jedes Mal Handler
3. Send „sub- genannt wird Anfrage“für jedes Objekt aus Reaktion

Anmerkungen:
1. Alle Anfragen (Eltern + Unteranfragen) sollte annulliert werden, sobald viewController geschlossen ist (deinit hat angerufen)
2. Ich muss eine Fähigkeit haben, Elternantrag jederzeit abzubrechen. Dies sollte auch alle Unteranforderungen abbrechen.

Was derzeit Ich habe

Ich weiß, was ich in "unendlichen Handler" zu tun ist irgendwie "duct tape", aber ich bin neu mit ReactiveSwift ...

self.tableView.addInfiniteScrollingWithActionHandler { [unowned self] in 
    self.tempMutableProperty.value = true 
} 

self.tempMutableProperty.producer.skipNil().flatMap(.latest) { [unowned self] tempValueThatIDontNeed in 
    return self.producerForParentRequest(offset: self.offset) 
     .take(during: self.reactive.lifetime) 
     .on(
      // handlers for errors, completed, etc 
      value: { [unowned self] items in 
       self.items.append(items) 
       self.tableView.reloadData() 
       self.offset += items.count 
       // SEND REQUEST #2 FOR EACH ITEM 
      } 
    ).flatMapError { error in 
     return SignalProducer.empty 
    } 
}.observe(on: UIScheduler().start() 

So, Wie Sie sehen, habe ich Paginierung mit TableView. Ich hole Liste von Objekten für jede Seite. Dann muss ich für jeden Gegenstand aus der Antwort eine zusätzliche Information mit der Anfrage # 2 holen.


Fluss und Probleme:
1. Natürlich habe ich von tempMutableProperty loswerden wollen und irgendwie neu starten parent request ohne some kinda von Proxy
2. Jeder sub-request unabhängig sein sollte, was bedeutet, ich möchte value/error Handler für jede sub-request getrennt haben, und nicht wie es auf alle 10 Unteranforderungen wartet und dann Erfolg Handler mit allen 10 Antworten gesammelt aufrufen. Darüber hinaus sollte der Fehler bei bestimmten Unteranfragen keine Auswirkungen auf andere Unteranfragen haben, die ausgeführt werden.
3. Der Benutzer kann seine Suchanfrage ändern, ohne auf den Abschluss des gesamten Anfrageprozesses zu warten. Dies bedeutet, dass, sobald der Benutzer einige Parameter ändert, werde ich alle Elemente löschen, und ich muss parent request innerhalb aller sub-requests abbrechen und alles wieder starten.
4. Zusätzlich zu # 2 kann der Benutzer manchmal nach unten scrollen, um neue Teile von Artikeln abzurufen. Dies bedeutet, dass die neue parent request starten sollte, aber sub-requests von früheren Antwort von parent request sollte weiter funktionieren
5. Alle Anfragen sollten auf self.deinit abgesagt werden, also soll dies alles nur während self.lifetime arbeiten, aber ich bin mir nicht sicher, was der richtige Ort, um diese Parameter zu setzen

Ich bin mir nicht sicher, ob dies möglich ist, ohne Einweg-Speicherung/Signale als Eigenschaften von Selbst, das ist also kein Problem, wenn sub-request irgendwie als Eigenschaften gespeichert werden.


Vielen Dank für Ihre Hilfe

Antwort

0

Also, ich werde hier posten Lösungen für meine Probleme.

Für Punkt 1 ich das gemacht habe:

let disposable = SerialDisposable() 

self.tableView.addInfiniteScrolling(actionHandler: { [unowned self] in 
    self.disposable.inner = nil // this is needed to force dispose current request before starting new one 
    self.disposable.inner = self.producer().take(during: self.reactive.lifetime) 
     .on(value: { [unowned self] value in 
      // handle as you want 
     }).start() 
}) 

Das half mir von tempMutableProperty loszuwerden. Anstelle von flatMap wird SerialDisposable verwendet. So funktioniert dies gut für mich, und es entsorgt Anfrage automatisch, wenn self wird

immer zerstört

Für andere Punkte ich das gemacht habe:

Die Idee war, dass ich einige Artikel für Tisch bin Laden , dann muss ich eine zusätzliche Anfrage pro Artikel senden, um detaillierte Informationen dafür zu erhalten. Also, ich habe Klasse erstellt, mit 3 Eigenschaften - item, itemDetail, requestSent.

Dann in willDisplay cell habe ich

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 
    if !self.items[indexPath.row].requestSent { 
     self.items[indexPath.row].details <~ self.detailedProducerForItemID(self.items[indexPath.row].item.id) 
     self.items[indexPath.row].requestSent = true 
    } 
} 

Hinweis: self.items[indexPath.row].details ein MutableProperty<Details?>

In der Zelle selbst ist Details zur Darstellung habe ich so etwas wie:

let (detailsSignal, detailsObserver) = Signal<MutableProperty<Details?>, NoError>.pipe(), wo Details das ist Name der Klasse für die Details des Artikels.

In Zelle awakeFromNib:

let details = self.detailsSignal.flatMap(.latest) { $0.producer } 

self.detailsLabel.reactive.text <~ details.map { value -> String? in 
    // handling `Details` 
} 

Und in cellForRow Ich nenne cell.detailsObserver.send(value: self.items[indexPath.row].details)

Und wenn VC deinit genannt wird, oder führe ich eine neue main Anfrage, alle Anfragen werden automatisch gelöscht zu werden, seit wann Ich benutze self.items.removeAll() es entsorgen alle Anfragen.

Dies ist ein rauer Fluss. Wenn jemand an mehr Details interessiert ist, zögern Sie nicht zu fragen.

0

Für Teil 1, würde ich eine Erweiterung hinzufügen, die die unendliche Scrollen Aktion in ein Signal Umgang dreht:

extension Reactive where Base: UITableView { 
    public func infiniteScrollingSignal() -> Signal<Void, NoError> 
    { 
     return Signal { [unowned base = self.base] observer in 
      base.addInfiniteScrollingWithActionHandler { 
       observer.send(value:()) 
      } 

      return ActionDisposable { 
       // Unsubscribe the infinite scrolling action handler here if necessary 
      } 
     } 
     .take(during: self.lifetime) 
    } 
} 

Dann können Sie alle Ihre Logik self.tableView.reactive.infiniteScrollingSignal()

+0

hmm hook up ... Diese ist ein bisschen besser als meine Lösung, da ich keine Art von Einweg-Eigenschaften benötigt.Aber es hat nur ein "kosmetisches Problem", wie es noch dummy booleschen Wert 'true' sendet, nur um dann' flatMap' closure zu haben. Wie auch immer, das ist ein guter Vorschlag, der den Code von ViewController klarer macht –

+1

Ich denke, Sie können den Typ von 'Signal ' nach 'Signal ' ändern und dann einen leeren Tupel-Wert senden: 'observer .send (Wert:()) '. Ich war mir nicht ganz sicher, also habe ich Bool benutzt. – jjoelson

+1

Die schnelle Art, mit Parametern umzugehen, die Sie nicht interessieren, ist, einfach einen Unterstrich zu verwenden, wie 'flatMap (.latest) {_ in ...}' – jjoelson