2017-06-13 5 views
3

Je suis plutôt nouveau à RxSwift et essaye de gérer la tâche de basculer entre l'état vide et l'état peuplé d'une saisie semi-automatique de recherche.Comment utiliser correctement switchLatest pour basculer entre les résultats de recherche et l'état vide de tableview?

J'ai un pilote qui répond à un texte textfield change avec longueur> 0 et fait des demandes de réseau et un autre que des filtres sur les requêtes de recherche vides et peupler la tableview avec "Favoris". J'ai d'abord utilisé merge() sur les deux observables mais le problème était que l'effacement rapide du texte montrerait les favoris mais quand la dernière requête de récupération reviendrait, elle fusionnerait et écraserait l'état vide.

J'ai essayé de passer à switchLatest() en espérant qu'il annulerait la requête de récupération précédente quand le dernier clearResult a été déclenché mais cette observable ne semble jamais se déclencher et je suis un peu coincé. Toute aide serait grandement appréciée!

let queryFetchResultViewModel: Driver<[NewSearchResultViewModel]> = queryText 
        .filter { $0.utf8.count > 0 } 
        .throttle(0.5, scheduler: MainScheduler.instance) 
        .flatMapLatest { query in 
         appContext.placeStore.fetchPredictions(withQuery: query) 
        } 
        .map { $0.map { NewSearchResultViewModel.prediction(prediction: $0) } } 
        .asDriver(onErrorJustReturn: []) 

let queryClearResultViewModel: Driver<[NewSearchResultViewModel]> = queryText 
         .filter { $0.utf8.count == 0 } 
         .withLatestFrom(favoritePlaces.asObservable()) 
         .map { places in places.map(NewSearchResultViewModel.place) } 
         .asDriver(onErrorJustReturn: []) 

searchResultViewModel = Driver.of(queryFetchResultViewModel, queryClearResultViewModel).switchLatest() 
+0

Pourquoi ne pas vous résoudre le problème initial avec 'fusion()'? Effacer rapidement le texte devrait annuler votre demande précédente. Et c'est une tâche facile – iWheelBuy

+0

Aussi, pourquoi étrangler, pas rebondir? – iWheelBuy

+0

@iWheelBuy J'ai d'abord essayé de fusionner mais je ne voyais pas le comportement "annuler la requête précédente" que vous avez décrit. Étant donné que la demande avec une demande réseau prend plus de temps que la charge immédiate des lieux favoris, elle reviendrait plus tard et écraserait l'événement de lieu favori. Je ne crois pas que le flux avec une demande de réseau fusionne réellement dans son événement jusqu'à ce qu'il revienne donc il n'y a rien à annuler pour le moment (du point de vue du flux fusionné). Cela a-t-il du sens? – Danny

Répondre

1

Vous attendez qu'une chaîne vide affecte la sortie d'un flux qui filtre explicitement les chaînes vides. C'est pourquoi vous avez ce problème.

--EDIT--

Pour mieux expliquer ce qui précède ... On suppose que l'utilisateur tape un caractère et supprime alors dans votre code d'origine. C'est ce qui va arriver: Le personnage ira dans le filter pour queryClearResultViewModel et sera filtré. Il passera également par la chaîne pour queryFetchResultViewModel et ira chercher une prédiction. Ensuite, l'utilisateur supprime le caractère qui entrera dans le filtre pour queryFetchResultViewModel et se font arrêter, en attendant, il va tout le chemin à travers le queryClearResultViewModel et le searchResultViewModel obtiendra les lieux préférés ...

Ensuite, la demande de réseau qui passe termine les résultats de la prédiction à searchResultViewModel.

À ce stade, switchLatest arrêtera le queryClearResultViewModel (il cessera d'écouter) et affichera les résultats de la requête. Il a cessé d'écouter le flux de résultats clair maintenant, donc plus de favoris ne se présentera jamais.

- FIN EDIT -

Voici le code qui va faire ce que vous voulez:

searchResultViewModel = queryText 
    .throttle(0.5, scheduler: MainScheduler.instance) 
    .flatMapLatest { query -> Observable<[NewSearchResultViewModel]> in 
     if query.isEmpty { 
      return favoritePlaces.asObservable() 
       .map { $0.map(NewSearchResultViewModel.place) } 
     } 
     else { 
      return appContext.placeStore.fetchPredictions(withQuery: query) 
       .map { $0.map { NewSearchResultViewModel.prediction(prediction: $0) } } 
     } 
    } 
    .asDriver(onErrorJustReturn: []) 

Le code ci-dessus fonctionne parce que flatMapLatest annulera l'appel fetchPredictions(withQuery:) en vol lorsqu'une nouvelle requête vient dans ça. Même si cette requête est vide.

Si vous êtes marié à diviser le queryText en deux courants, puis les recombiner, tout ce que vous devez faire est de permettre à l'queryFetchResultViewModel pour traiter le texte vide en émettant un Observable.empty() lorsqu'une requête vide qui en sort. Mais même là, vous pourriez rencontrer des problèmes de course parce que le fetch est étranglé alors que le clear ne l'est pas. Vous pouvez donc vous retrouver dans une situation où le fetch commence, puis la requête vide qui affiche les favoris, puis la requête qui affiche les résultats, puis le flux de récupération reçoit le clear (retardé) et ne fait rien car la requête est déjà terminée . Donc, vous auriez à étrangler le flux clair ainsi que le flux de fetch ...

donc quelque chose comme ça (j'ai utilisé debounce parce que je pense que cela fonctionne mieux pour ce genre de choses):

let debouncedQuery = queryText.debounce(0.5, scheduler: MainScheduler.instance) 

let queryFetchResultViewModel = debouncedQuery 
    .flatMapLatest { query -> Observable<[String]> in 
     guard !query.isEmpty else { return Observable.empty() } 
     return appContext.placeStore.fetchPredictions(withQuery: query) 
    } 
    .map { $0.map { NewSearchResultViewModel.prediction(prediction: $0) } } 
    .asDriver(onErrorJustReturn: []) 

let queryClearResultViewModel = debouncedQuery 
    .filter { $0.isEmpty } 
    .withLatestFrom(favoritePlaces.asObservable()) 
    .map { $0.map(NewSearchResultViewModel.place) } 
    .asDriver(onErrorJustReturn: []) 

searchResultViewModel = Driver.merge(queryFetchResultViewModel, queryClearResultViewModel) 
+0

Ah intéressant! J'ai fini par aller avec votre suggestion initiale d'un conditionnel dans flatmap, mais je ne suis pas encore tout à fait sûr que je comprends votre premier point de pourquoi switchLatest ne fonctionne pas. Le flux de sortie (searchResultViewModel) n'effectue aucun filtrage, il suffit de basculer entre les flux, n'est-ce pas? Quand je vais à partir d'une requête de longueur 2 à zéro ne devrait pas les événements de sortie queryFetchResultViewModel 2, puis la sortie queryClearResultViewModel un? Heureux avec votre suggestion initiale mais toujours curieux! – Danny

+1

J'ai édité ma réponse avec une explication étape par étape de ce qui se passe. –