Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 使用反应式编程将带有imageURL的对象转换为带有已下载UIImage的对象_Ios_Swift_Reactive Programming_Rx Swift_Reactive Swift - Fatal编程技术网

Ios 使用反应式编程将带有imageURL的对象转换为带有已下载UIImage的对象

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? } 因此,尽管我对

我有一个对象数组。我想把它转换成一个包含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?
}

因此,尽管我对上下文的评论可能非常重要,但以下是我如何使用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操作符所做工作的摘要:

  • SignalProducer.init(值:)
    相当于RxSwift的
    可观察的.from
    。它创建一个生产者,将序列的每个值作为单独的事件发送
  • collect
    相当于RxSwift的
    toArray
    。它从源生产者收集每个值,并在源生产者完成后在单个数组中发送它们
  • flatMap
    为每个传入的
    A
    启动
    convertAToB
    生产者,并根据指定的
    flattstrategy
    合并结果。在本例中,我使用了
    .concat
    ,这相当于RxSwift的
    concatMap
    ,它连接每个结果并保留@PhilippeC在回答中描述的顺序

使用RxSwift,可以使用两个操作符完成此操作:

  • 可观察。from
    toArray()
    运算符,用于将数组转换为事件序列并来回转换
  • 一种运算符,它将每个
    A
    项进行转换,然后将每个单独的结果连接成一个可观察序列,并尊重原始顺序
以下是Swift代码:

// 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
并手动排序结果,您可以同时执行图像获取。