【问题标题】:RxSwift combine observable with conditionalRxSwift 结合 observable 和条件
【发布时间】: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()'

标签: conditional observable rx-swift


【解决方案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 被调用,那么它应该尝试远程状态覆盖。我已经用可行的方法更新了我原来的问题,我只是不知道是否有另一种方法可以有条件地组合可观察对象。
猜你喜欢
  • 2016-03-12
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 2021-02-06
相关资源
最近更新 更多