2

我有一个 A 对象数组。我想将它转换为具有 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?
}
4

2 回答 2

4

因此,尽管我对这可能很重要的上下文发表了评论,但这就是我将如何异步转换[A][B]使用 ReactiveSwift。请注意,我没有机会测试此代码,但它应该涵盖基本思想:

// 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]
}

编辑:

为了使这个答案与@PhilippeC 的 RxSwift 答案相提并论,这里总结了每个 ReactiveSwift 操作员正在做的事情:

  • SignalProducer.init(values:)相当于 RxSwift 的Observable.from. 它创建了一个生产者,将序列的每个值作为一个单独的事件发送。
  • collect相当于 RxSwift 的toArray. 它从源生产者收集每个值,并在源生产者完成后将它们发送到单个数组中。
  • flatMapconvertAToB为每个传入启动生产者A,并根据指定合并结果FlattenStrategy。在这种情况下,我使用.concat了 ,这使得它等同于 RxSwift 的concatMap连接每个结果并保留@PhilippeC 在他的回答中描述的顺序。
于 2018-05-07T13:30:29.070 回答
0

使用 RxSwift,这可以使用几个运算符来完成:

  • 和运算符将数组转换为事件序列,Observable.fromtoArray()来回转换。
  • concatMap运算符转换每个单独的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

Observable<[B]>最后,您将得到作为单个事件预期的结果数组。要使用它,只需订阅此 Observable,或将其直接绑定到您的 UI 或 DataSource。

注意,如果数组很长,我还建议在后台队列上运行下载:感谢 Rx,这可以通过添加类似.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))after的内容轻松完成.toArray()

于 2018-05-10T22:02:18.627 回答