Reactive programming 使用UI控件过滤RxSwift/RxCocoa中的反应式数据集

Reactive programming 使用UI控件过滤RxSwift/RxCocoa中的反应式数据集,reactive-programming,rx-swift,Reactive Programming,Rx Swift,(我对这里的反应式编程非常陌生,我知道我还没有完全以“反应式”的方式思考,所以我不确定我是否做对了……) 我有一个来自REST API的数据集,用于驱动Photos的UICollectionView,并通过UI中的开关对集合进行过滤。我当前的绑定图看起来像 [API DataSet] -> unfilteredData: BehaviorRelay<Photo> -map-> collectionView.items 处理这样的问题最“被动”的方式是什么 编辑 正如我在

(我对这里的反应式编程非常陌生,我知道我还没有完全以“反应式”的方式思考,所以我不确定我是否做对了……)

我有一个来自REST API的数据集,用于驱动
Photo
s的UICollectionView,并通过UI中的开关对集合进行过滤。我当前的绑定图看起来像

[API DataSet] -> unfilteredData: BehaviorRelay<Photo> -map-> collectionView.items
处理这样的问题最“被动”的方式是什么

编辑
正如我在下面的评论中所提到的,“只有一个开关。有来自服务器的输入JSON数据进入集合视图,还有一个开关更改显示哪些照片(在显示所有照片之间切换,或者仅在该实例中附加了位置的照片子集之间切换)”在本例中,您将显示两个事件序列:api调用和开关值。您需要的是,每当交换机发出一个值时,您希望“记住”来自网络呼叫的最新响应,并根据交换机值进行过滤。以下是如何做到这一点:

让filteredPhotos=Observable.combinelatetest(unfilteredDataSource.asObservable(),
filteringSwitch.rx.isOn){($0,$1)}//1
.map{照片,包括
photos.filter{$0.hasLocation | | includeal}
}
.asDriver(onErrorJustReturn:[])
滤镜照片
.drive(onNext:{…})
.处置(由:处置人)
//1-每次一个可观测对象发出一个值时,它调用闭包,将最新的值组合成一个图。您不必担心进行另一个api调用

有几点值得注意:

  • CombineTest将等待,直到其每个可观察对象发出一个值,并且仅在最后一个序列完成时完成
  • 此外,如果任何序列完成,它将使用最后发出的值
  • 当您不确定某个事件流时,应该使用(请注意,您可以拖动它们)
  • (几乎)一切都可以是一个序列

考虑到这一点,还有更多的改进空间。我看到您有一个refreshData()函数,它也可以转换为事件流。您可能在
viewDidLoad
viewDidAppear
中调用此函数,或者可能在
UIRefreshControll
中调用此函数。但是它们是触发器,所以它们是一系列的
Void
,对吗?这意味着您可以将最新版本映射到api调用中。这样,您也不再需要继电器

所以我看到有两个(或多个)开关,但我不确定它们是如何相互影响的。您需要使用CombineTest或withLatestFrom,但从代码片段中,您使用filteringSwitch只是作为触发器?或者它也在改变includeAllSwitch,而您在其他地方也必须这样做?不,只有一个开关。有来自服务器的输入JSON数据进入集合视图,还有一个开关更改显示哪些照片(在显示所有照片之间切换,或者仅在该实例中附加位置的照片子集之间切换),非常好!非常感谢。至于关于在api调用中使用
flatMapLatest
而不再需要
unfilteredDataSource
的评论,我将去看看我是否能对此有所了解。。。稍后可能会带着更多问题回来;-)为了向未来的搜索者说明最后一个想法,我制作了一个func
pullAPIData()->Observable
,它调用API并返回输出Observable,然后在
setupRx()
期间将其平面映射到一个本地Observable,就像这样:
let dataSource=refreshButton.rx.tap.flatMapLatest{self.pullAPIData()}
。然后我使用这个本地
数据源
可观察,不再需要
未过滤的数据源
浮动。
private let unfilteredDataSource = BehaviorRelay<[Photo]>(value: [])

/// Refresh data from API
private func refreshData() {
    let photoResponse: Observable<[Photo]> = APICall(method: .GET, path: "photos.json")
    photoResponse
        .bind(to: unfilteredDataSource)
        .disposed(by: disposeBag)
}

/// Setup reactive bindings (on viewDidLoad)
private func setupRx() {
    // unfiltered becomes filtered via map before it's sent to collection items
    unfilteredDataSource
        .observeOn(MainScheduler.asyncInstance)
        .map {
            // filtering by hasLocation if includeAllSwitch is off
            $0.filter{ $0.hasLocation() || self.includeAllSwitch.isOn } //1
        }
        .bind(to: photoCollection.rx.items(cellIdentifier: "MyCell", cellType: PhotoCell.self)) { (row, element, cell) in
            // here I set up the cell...
        }
        .disposed(by: disposeBag)

    // filteringSwitch.isOn changes need to drive filtering too
    filteringSwitch.rx
        .isOn
        .asDriver(onErrorJustReturn: true) // Driver ensures main thread, and no errors (always returns true here)
        .drive(onNext: { [unowned self] switchValue in
            // triggering events using A = A; seems clumsy? And switchValue unused.
            self.unfilteredDataSource.accept(self.unfilteredDataSource.value) //2
        })
        .disposed(by: disposeBag)
}