如何正确使用 switchLatest 在搜索结果和 tableview 的空状态之间切换?

How to properly use switchLatest to toggle between search results and empty state of tableview?

我是 RxSwift 的新手,正在尝试处理在搜索自动完成的空状态和填充状态之间切换的任务。

我有一个驱动程序响应长度 > 0 的文本字段文本更改并发出网络请求,而另一个驱动程序不是对空搜索查询进行过滤,而是只用 "Favorites" 填充表格视图。我最初在两个可观察对象上使用了 merge() 但问题是快速清除文本会显示收藏夹但是当最后一个获取请求返回时它会合并并覆盖空状态。

我尝试切换到 switchLatest(),希望它能在触发最终的 clearResult 时取消之前的获取请求,但这个可观察对象似乎根本不会触发,我有点卡住了。任何帮助将不胜感激!

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

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

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

您期望空字符串会影响显式过滤掉空字符串的流的输出。这就是您遇到此问题的原因。

--编辑--

为了更好地解释以上... 假设用户键入一个字符,然后在您的原始代码中将其删除。这就是将要发生的事情:角色将进入 filter for queryClearResultViewModel 并被过滤掉。它还将遍历 queryFetchResultViewModel 的链并获取预测。然后用户删除将进入 queryFetchResultViewModel 过滤器的字符并停止,同时它会一直通过 queryClearResultViewModel 并且 searchResultViewModel 将获得最喜欢的地方.. .

然后网络请求完成,将预测结果传递给searchResultViewModel

此时,switchLatest 将关闭 queryClearResultViewModel(它将停止监听)并显示查询结果。它现在已停止收听清晰的结果流,因此不会再显示更多收藏夹。

-- 结束编辑--

这是可以满足您要求的代码:

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

以上代码有效,因为 flatMapLatest 将在新查询进入时取消 in-flight fetchPredictions(withQuery:) 调用。即使该查询为空。

如果您打算将 queryText 分成两个流,然后 re-combining 它们,那么您需要做的就是允许 queryFetchResultViewModel 通过发出来处理空文本Observable.empty() 当空查询通过时。但即使在那里,您也可能 运行 陷入竞争问题,因为获取正在受到限制,而清除却没有。因此,您可以 运行 进入以下情况:获取开始,然后显示收藏夹的空查询通过,然后显示结果的查询完成,然后获取流接收(延迟的)清除并且不执行任何操作,因为查询已经完成。所以你必须限制清流和获取流......

所以像这样(我使用去抖动是因为我认为它对这类事情更有效):

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 { [=11=].map { NewSearchResultViewModel.prediction(prediction: [=11=]) } }
    .asDriver(onErrorJustReturn: [])

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

searchResultViewModel = Driver.merge(queryFetchResultViewModel, queryClearResultViewModel)