Ios 使用反应式编程将带有imageURL的对象转换为带有已下载UIImage的对象
我有一个对象数组。我想把它转换成一个包含B类型对象的数组。但棘手的部分是同时下载图像,并使用RxSwift或ReactiveSwift执行所有操作。你有什么建议我怎么做Ios 使用反应式编程将带有imageURL的对象转换为带有已下载UIImage的对象,ios,swift,reactive-programming,rx-swift,reactive-swift,Ios,Swift,Reactive Programming,Rx Swift,Reactive Swift,我有一个对象数组。我想把它转换成一个包含B类型对象的数组。但棘手的部分是同时下载图像,并使用RxSwift或ReactiveSwift执行所有操作。你有什么建议我怎么做 struct A { let name: String let imageURL: URL let thumbnailURL: URL } struct B { let name: String let image: UIImage? let thumbnail: UIImage? } 因此,尽管我对
struct A {
let name: String
let imageURL: URL
let thumbnailURL: URL
}
struct B {
let name: String
let image: UIImage?
let thumbnail: UIImage?
}
因此,尽管我对上下文的评论可能非常重要,但以下是我如何使用ReactiveSwift将
[a]
异步转换为[B]
。请注意,我还没有机会测试这段代码,但它应该能够理解基本思想:
// This function takes an `NSURL` and creates an asynchronous `SignalProducer` to
// download an image from that URL or yields `nil` if there is an error.
func downloadImage(url: URL) -> SignalProducer<UIImage?, NoError> {
let request = URLRequest(url: url)
return URLSession.shared.reactive.data(with: request)
.map { (data, response) -> UIImage? in UIImage(data: data) }
.flatMapError { _ in SignalProducer<UIImage?, NoError>(value: nil) }
}
func convertAToB(_ a: A) -> SignalProducer<B, NoError> {
let imgDownload = downloadImage(url: a.imageURL)
let thumbDownload = downloadImage(url: a.thumbnailURL)
return SignalProducer<UIImage?, NoError>.combineLatest(imgDownload, thumbDownload)
.map { images in
return B(name: a.name, image: images.0, thumbnail: images.1)
}
}
func convertAllAsToBs(_ inputs: [A]) -> SignalProducer<[B], NoError> {
return SignalProducer<A, NoError>(values: inputs)
.flatMap(.concat, convertAToB)
.collect()
}
let inputs: [A] = ...
convertAllAsToBs(inputs).startWithValues { outputs in
// `outputs` is [B]
}
//此函数接受一个'NSURL',并创建一个异步'SignalProducer',以
//从该URL下载图像,如果出现错误,则生成'nil'。
func下载图像(url:url)->SignalProducer{
let request=URLRequest(url:url)
返回URLSession.shared.reactive.data(带:request)
.map{(数据,响应)->UIImage中的UIImage(数据:数据)}
.flatMapError{在信号发生器中(值:nil)}
}
func convertAToB(a:a)->SignalProducer{
让imgDownload=downloadImage(url:a.imageURL)
让thumbDownload=downloadImage(url:a.thumbnailURL)
返回信号生成器.CombineTest(imgDownload,thumbDownload)
.map{中的图像
返回B(名称:a.name,图像:images.0,缩略图:images.1)
}
}
func convertAllAsToBs(u输入:[A])->SignalProducer{
返回信号发生器(值:输入)
.flatMap(.concat,convertAToB)
.collect()
}
让输入:[A]=。。。
convertAllAsToBs(输入).startWithValues{输出
//`outputs`是[B]
}
编辑:
为了使这个答案与@PhilippeC的RxSwift答案相一致,下面是每个ReactiveSwift操作符所做工作的摘要:
相当于RxSwift的SignalProducer.init(值:)
。它创建一个生产者,将序列的每个值作为单独的事件发送可观察的.from
相当于RxSwift的collect
。它从源生产者收集每个值,并在源生产者完成后在单个数组中发送它们toArray
为每个传入的flatMap
启动A
生产者,并根据指定的convertAToB
合并结果。在本例中,我使用了flattstrategy
,这相当于RxSwift的.concat
,它连接每个结果并保留@PhilippeC在回答中描述的顺序concatMap
和可观察。from
运算符,用于将数组转换为事件序列并来回转换toArray()
- 一种运算符,它将每个
项进行转换,然后将每个单独的结果连接成一个可观察序列,并尊重原始顺序A
// choose the implementation you prefer for this function
// N.B. : if using RxCocoa, prefer Single<UIImage?> as return type
func downloadImage(url: URL) -> Observable<UIImage?> {
return URLSession.shared.rx
.data(URL)
.map { data in UIImage(data: data) }
.catchErrorJustReturn(nil)
}
let arrayOfA: [A] = []; // your input array goes here.
let arrayOfB: Observable<[B]> =
Observable
// Convert each array element to an item
.from(arrayOfA)
// concatMap preserves the order
.concatMap { a in
Observable.zip(downloadImage(a.imageURL), downloadImage(a.thumbnailURL))
.map { image, thumbnail in
B(name: a.name, image: image, thumbnail: thumbnail)
}
}
.toArray()
// do some stuff with the result: arrayOfB
//选择此函数的首选实现
//注意:如果使用RxCocoa,则首选单一返回类型
func下载图像(url:url)->可观察{
返回URLSession.shared.rx
.数据(URL)
.map{UIImage中的数据(数据:数据)}
.catchErrorJustReturn(无)
}
让arrayOfA:[A]=[];//你的输入数组在这里。
let arrayOfB:可观察=
可观察
//将每个数组元素转换为一个项
.来自(arrayOfA)
//concatMap保留顺序
.concatMap{a in
.zip(downloadImage(a.imageURL)、downloadImage(a.thumbnailURL))
.map{图像,缩略图在
B(名称:a.名称,图像:图像,缩略图:缩略图)
}
}
.toArray()
//用结果做一些事情:arrayOfB
最后,您将得到一个可观察的数组,该数组预期为单个事件。要使用它,只需订阅这个可观察对象,或者将它直接绑定到您的UI或数据源
注意:对于长数组,我还建议在后台队列上运行下载的:多亏了Rx,只需在.toArray()之后添加类似.subscribeOn(ConcurrentDispatchQueueScheduler(qos:.background))
的内容即可轻松完成
您是想在异步加载这些图像时显示它们,就像在表视图中一样,还是只想在它们全部下载后创建一个最终数组?这将有助于了解更多关于最终目标的上下文。不过,有一句话-.concat
为了保持订单,需要付出代价-您将保留下一个请求,直到收到当前请求的响应。使用.merge
并手动排序结果,您可以同时执行图像获取。