RxSwift如何拆分进度和结果可观察值?

时间:2019-09-07 11:57:02

标签: reactive-programming rx-swift

我需要基于String输入进行长时间的异步计算,并返回一个大的Data实例。 我使用Single特性来实现这一点:

func calculateData(from: String) -> Single<Data>

这个例子很简单并且有效。但是我还需要跟踪进度-一个介于0和1之间的数字。我正在执行以下操作:

func calculateData(from: String) -> Observable<(Float, Data?)>

我得到以下顺序:

next: (0, nil)
next: (0.25, nil)
next: (0.5, nil)
next: (0.75, nil)
next: (1, result data)
complete

我检查进度和数据以了解是否有结果,它可以正常工作,但是我在这里感觉到强烈的气味。我想分离流:Observable和进度,Single和结果。我知道我可以返回带有两个可观察值的元组或结构,但是我也不喜欢这样。

我该如何实现?有可能吗?

1 个答案:

答案 0 :(得分:3)

尽管我可以命名元组中的元素,但您拥有的没什么


func calculateData(from: String) -> Observable<(percent: Float, data: Data?)>

let result = calculateData(from: myString)
    .share()

result
    .map { $0.percent }
    .subscribe(onNext: { print("percent complete:", $0) }
    .disposed(by: disposeBag)

result
    .compactMap { $0.data }
    .subscribe(onNext: { print("completed data:", $0) }
    .disposed(by: disposeBag)

另一种选择是使用一个返回完整百分比或数据的枚举:

enum Progress {
    case incomplete(Float)
    case complete(Data)
}

func calculateData(from: String) -> Observable<Progress>

但是,这样做会使将Observable分解为两个流变得更加困难。为此,您必须像这样扩展Progress:

extension Progress {
    var percent: Float {
        switch self {
        case .incomplete(let percent):
            return percent
        case .complete:
            return 1
        }
    }

    var data: Data? {
        switch self {
        case .incomplete:
            return nil
        case .complete(let data):
            return data
        }
    }
}

如您所见,执行上述操作实质上会将枚举转换为您已经在使用的元组。这样做的好处是,您获得了编译时的保证,即如果发出Data,则进度为1。

如果您希望两全其美,请使用结构:

struct Progress {
    let percent: Float
    let data: Data?

    init(percent: Float) {
        guard 0 <= percent && percent < 1 else { fatalError() }
        self.percent = percent
        self.data = nil
    }

    init(data: Data) {
        self.percent = 1
        self.data = data
    }
}

func calculateData(from: String) -> Observable<Progress>

以上内容提供了枚举的编译时间保证,并简化了元组的拆分过程。它还提供运行时保证,进度将为0 ... 1,如果进度为1,则数据将存在。