RxSwift 结合 observable 和条件

Posted

技术标签:

【中文标题】RxSwift 结合 observable 和条件【英文标题】:RxSwift combine observable with conditional 【发布时间】:2017-08-25 02:02:15 【问题描述】:

我正在尝试组合 observables,并且我希望它们按顺序运行(例如,执行步骤 1,如果满足某些条件则执行步骤 2,如果满足某些条件则执行步骤 3)。我发现这样做的唯一方法是在每个步骤中添加条件,我不喜欢这样做:这是我当前解决方案的示例:

enum Status 
    case unknown, exists, missing


func refresh() -> Observable<Status> 
    return checkLocalStatus()
        .flatMapLatest  $0 == .exists ? Observable.just($0) : self.attemptRemoteStatusOverride() 
        .flatMapLatest  $0 == .exists ? Observable.just($0) : self.attemptRemoteStatusUpdate() 


private func checkLocalStatus() -> Observable<Status> 
    return Observable.create  observer in
        // Regarding Maxim Volgin's comment, here I'm converting a closure to an 
        // observable... why not use Observable.create?
        self.cache.status  (status) in
            guard status != .exists else 
                observer.onNext(status) // .exists
                observer.onCompleted()
            

            /* I don't want this condition to be here */
            if ignoreRemote 
                // status is !exists and we should ignore remote, throw error
                observer.onError(Errors.remoteDisabled)
            

            observer.onNext(.missing)
            observer.onCompleted()
        
    


private func attemptRemoteStatusOverride() -> Observable<Status> 
    return remote.statusOverride()


private func attemptRemoteStatusUpdate() -> Observable<Status> 
    return Observable.create  observer in
        // Regarding Maxim Volgin's comment, here I'm converting a closure to an 
        // observable... why not use Observable.create?
        self.remote.updateStatus  (status, error) in
            guard error == nil else 
                observer.onError(error!)
            
            observer.onNext(status)
            observer.onCompleted()
        
    

我想做这样的事情:

func refresh() -> Observable<Status> 
    return checkLocalStatus()
        .if( $0 != .exists && !ignoreRemote ,
            then:  self.attemptRemoteStatusOverride() ,
            else:  return $0 )
        .if( $0 != .exists ,
            then:  self.attemptRemoteStatusUpdate() ,
            else:  return $0 )

func refresh() -> Observable<Status> 
    return checkLocalStatus()
        .flatMapLatest(if:  $0 != .exists && !ignoreRemote )  self.attemptRemoteStatusOverride() 
        .flatMapLatest(if:  $0 != .exists )  self.attemptRemoteStatusUpdate() 

我找不到任何像我正在尝试的东西,所以我认为我做错了。有没有人对如何走这条组合可观察的路线有建议或替代方案?我已经看到使用combineLatest 并根据其他结果返回一些结果的示例,但我只想在满足条件时执行每个步骤。 combineLatest 将执行每个步骤(每次),然后我将根据其他步骤的输出返回某些步骤的结果。我也开始考虑编写自定义运算符,但想不出办法。

更新:我已更改为以下内容并计划编写一个删除重复的方法:

func refresh() -> Observable<Status> 
    return checkLocalStatus()
        .flatMapLatest  status -> Observable<Status>
            guard status != .exists && !ignoreRemote else 
                return Observable.just(status)
            
            return self.attemptRemoteStatusOverride()
        
        .flatMapLatest  status -> Observable<Status>
            guard status != .exists && !ignoreRemote else 
                return Observable.just(status)
            
            return self.attemptRemoteStatusUpdate()
        

【问题讨论】:

作为一般规则,除非您正在构建库/框架,否则不应使用“Observable.create()”。其次,你的‘Observable’只发出一个‘onNext’事件,那么为什么不用‘Single’而不是‘Observable’呢?您能否说明您打算如何使用这些可观察对象?因为你似乎在尝试解决一个你一开始就不应该遇到的问题。 为什么不使用Observable.create() 是“一般规则”?我有服务外观,我想返回 observables 而不是使用闭包。这似乎是 RxSwift 的常见模式。我在哪里可以阅读有关此规则的更多信息?我不知道Single,所以这很有帮助。 基本上,因为它是更多的工作。在像您这样的简单情况下,首选的解决方案是使用“.just()”或“.from()”。对于服务外观,最好使用 '.switchEmpty()'、'.retryError()'、'.catchErrorJustReturn()' 和我个人最喜欢的 '.switchLatest()' 【参考方案1】:

也许您需要一些带有条件的 flatMapLatest 函数版本?你可以用你想要的语法来做一些你想要的功能:

extension Observable 

    func flatMapLatest(condition: @escaping (E) -> Bool, then: @escaping (E) -> Observable, otherwise: @escaping () -> Observable) -> Observable 
        let observable = self.shareReplayLatestWhileConnected()
        let observableCondition = observable.map( condition($0) ).shareReplayLatestWhileConnected()
        let observableThen: Observable<E> = observableCondition
            .filter( $0 )
            .withLatestFrom(observable)
            .flatMapLatest( then($0) )
            .shareReplayLatestWhileConnected()
        let observableOtherwise: Observable<E> = observableCondition
            .filter( !$0 )
            .withLatestFrom(observable)
            .flatMapLatest( _ in otherwise() )
            .shareReplayLatestWhileConnected()
        return Observable<Observable<E>>
            .from([observableThen, observableOtherwise])
            .merge()
    

并使用它

func refresh() -> Observable<Status> 
    let condition =  (status: Status) -> Bool in
        return status == .exists
    
    let then =  (status: Status) -> Observable<Status> in
        return Observable.just(status)
    
    return checkLocalStatus()
        .flatMapLatest(condition: condition, then: then, otherwise: self.attemptRemoteStatusOverride)
        .flatMapLatest(condition: condition, then: then, otherwise: self.attemptRemoteStatusUpdate)

【讨论】:

这是有道理的,但是,我希望这些私有方法做他们应该做的任何事情,这样它们就可以在其他地方调用。我希望条件逻辑保留在 refresh 方法中。如果attemptRemoteStatusOverride 被调用,那么它应该尝试远程状态覆盖。我已经用可行的方法更新了我原来的问题,我只是不知道是否有另一种方法可以有条件地组合可观察对象。

以上是关于RxSwift 结合 observable 和条件的主要内容,如果未能解决你的问题,请参考以下文章

RxSwift。结合最新。并不是所有的 observables 都发出了

在 RxSwift 中处理嵌套的 observables

使用 if/else 语句返回 observable 不适用于 RxSwift flatMap

RxSwift 系列

Observable 的 RxSwift 用途

RxSwift - 如果满足条件,则阻止/停止/忽略事件