我在 Reactive 编程和 RxSwift 方面仍然是一个初学者。 我想链接两个不同的操作。在我的例子中,我只想从网络服务器下载一个 zip 文件,然后在本地解压缩。 我也想,同时显示下载文件的进度。 所以我开始创建第一个可观察对象:
class func rx_download(req:URLRequestConvertible, testId:String) -> Observable<Float> {
let destination:Request.DownloadFileDestination = ...
let obs:Observable<Float> = Observable.create { observer in
let request = Alamofire.download(req, destination: destination)
request.progress { _, totalBytesWritten, totalBytesExpectedToWrite in
if totalBytesExpectedToWrite > 0 {
observer.onNext(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
}
else {
observer.onNext(0)
}
}
request.response { _, response, _, error in
if let responseURL = response {
if responseURL.statusCode == 200 {
observer.onNext(1.0)
observer.onCompleted()
} else {
let error = NSError(domain: "error", code: responseURL.statusCode, userInfo: nil)
observer.onError(error)
}
} else {
let error = NSError(domain: "error", code: 500, userInfo: nil)
observer.onError(error)
}
}
return AnonymousDisposable () {
request.cancel()
}
}
return obs.retry(3)
}
之后,我为解压缩创建了一个类似的函数
class func rx_unzip(testId:String) -> Observable<Float> {
return Observable.create { observer in
do {
try Zip.unzipFile(NSURL.archivePath(testId), destination: NSURL.resourceDirectory(testId), overwrite: true, password: nil)
{progress in
observer.onNext(Float(progress))
}
} catch let error {
observer.onError(error)
}
observer.onCompleted()
return NopDisposable.instance
}
}
现在我在“ View 模型层”上有这个逻辑,所以我下载->订阅完成->解压
我想要的是将两个 Observable 合二为一,以便先执行下载,然后完成解压文件。有什么办法吗?
最佳答案
Concat
运算符需要相同的数据类型
的确,concat
运算符允许您强制执行可观察对象的序列,但是使用 concat
可能会遇到一个问题是concat
运营商要求 Observable
s 具有相同的通用类型。
let numbers : Observable<Int> = Observable.from([1,2,3])
let moreNumbers : Observable<Int> = Observable.from([4,5,6])
let names : Observable<String> = Observable.from(["Jose Rizal", "Leonor Rivera"])
// This works
numbers.concat(moreNumbers)
// Compile error
numbers.concat(names)
FlatMap 运算符允许您链接 Observable
的序列s
这是一个例子。
class Tag {
var tag: String = ""
init (tag: String) {
self.tag = tag
}
}
let getRequestReadHTML : Observable<String> = Observable
.just("<HTML><BODY>Hello world</BODY></HTML>")
func getTagsFromHtml(htmlBody: String) -> Observable<Tag> {
return Observable.create { obx in
// do parsing on htmlBody as necessary
obx.onNext(Tag(tag: "<HTML>"))
obx.onNext(Tag(tag: "<BODY>"))
obx.onNext(Tag(tag: "</BODY>"))
obx.onNext(Tag(tag: "</HTML>"))
obx.onCompleted()
return Disposables.create()
}
}
getRequestReadHTML
.flatMap{ getTagsFromHtml(htmlBody: $0) }
.subscribe (onNext: { e in
print(e.tag)
})
注意如何 getRequestReadHTML
类型为 Observable<String>
而函数 getTagsFromHtml
类型为 Observable<Tag>
.
使用多个 flatMaps 可以增加发射频率
但是要小心,因为 flatMap
运算符接受一个数组(例如 [1,2,3])或一个序列(例如一个 Observable),并将所有元素作为发射发射。这就是为什么已知会产生 1...n
的转换。 .
如果您定义了一个可观察对象,例如网络调用,并且您确定只有一次发射,您将不会遇到任何问题,因为它的转换是 1...1
。 (即一个 Observable 对应一个 NSData)。太棒了!
但是,如果你的 Observable 有多个发射,要非常小心,因为链式 flatMap
运营商将意味着排放量将呈指数级(?)增加。
一个具体的例子是当第一个 observable 发出 3 次发射时,flatMap 运算符转换 1...n
其中 n = 2,这意味着现在总共有 6 个排放量。另一个 flatMap 运算符可以再次转换 1...n
其中 n = 2,这意味着现在总共有 12 个排放量。仔细检查这是否是您预期的行为。
关于ios - RxSwift,我如何链接不同的可观察对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35936387/