我需要基于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
和结果。我知道我可以返回带有两个可观察值的元组或结构,但是我也不喜欢这样。
我该如何实现?有可能吗?
答案 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,则数据将存在。