我仍然是Reactive编程的初学者,而且RxSwift一般。 我想连锁两个不同的操作。在我的情况下,我只想从Web服务器下载一个zip文件,然后在本地解压缩。 我也希望,同时显示下载文件的进度。 所以我开始创建第一个observable:
myapp.example/**
之后,我为解压缩
创建了一个类似的函数 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)
}
现在我在“查看模型层”上有这个逻辑,所以我下载 - &gt;订阅完成 - &gt;解压
我想要的是将两个Observable合二为一,以便先执行下载,然后再完成解压缩文件。有没有办法做到这一点?
答案 0 :(得分:4)
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)
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>
的类型为flatMap
。
要小心,因为1...n
运算符接收数组(例如[1,2,3])或序列(例如Observable)并将所有元素作为发射发出。这就是为什么产生1...1
的转换。
如果您定义了一个可观察的网络调用,并且您确定只有一个发射,那么您将不会遇到任何问题,因为它的转换是flatMap
(即一个Observable到一个NSData)。太好了!
但是,如果您的Observable有多个排放,请非常小心,因为链式1...n
运营商将意味着排放将呈指数增长(?)。
一个具体的例子是当第一个观察者发射3个发射时,flatMap算子变换1...n
,其中n = 2,这意味着现在共有6个发射。另一个flatMap运算符可以再次转换ts
,其中n = 2,这意味着现在总共有12个发射。仔细检查这是否是您的预期行为。
答案 1 :(得分:1)
您可以使用concat
运算符链接这两个Observable。生成的Observable将从第一个发送next
个值,并在完成时从第二个发送rx_download
个值。
有一点需要注意:您将从rx_unzip
获得从0.0到1.0的进度值,然后map
的进度将从0.0开始。如果要在单个进度视图上显示进度,这可能会使用户感到困惑。
可能的方法是显示描述正在发生的事情的标签以及进度视图。您可以concat
每个Observable到包含进度值和描述文本的元组,然后使用let mappedDownload = rx_download.map {
return ("Downloading", $0)
}
let mappedUnzip = rx_download.map {
return ("Unzipping", $0)
}
mapped1.concat(mapped2)
.subscribeNext({ (description, progress) in
//set progress and show description
})
。它看起来像这样:
Mat
当然,有许多可能的解决方案,但这更像是一个设计问题而不是编码问题。