Преобразуйте объекты с imageURL в объекты с загруженным UIImage, используя реактивное программирование - PullRequest
0 голосов
/ 06 мая 2018

У меня есть массив объектов. Я хотел бы преобразовать его в массив с объектами типа 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?
}

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

С помощью RxSwift это можно сделать с помощью пары операторов:

  • операторы Observable.from и toArray(), которые преобразуют массив в последовательность событий и обратно.
  • оператор concatMap , который преобразует каждый отдельный 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

В конце вы получите результирующий массив в виде Observable<[B]>, который ожидается для одного события. Чтобы использовать его, просто подпишитесь на этот Observable или привяжите его напрямую к вашему пользовательскому интерфейсу или источнику данных.

N.B. в случае длинного массива я также предлагаю запускать загруженную в фоновую очередь: благодаря Rx это можно легко сделать, просто добавив что-то вроде .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background)) после .toArray().

0 голосов
/ 07 мая 2018

Итак, несмотря на мой комментарий о контексте, который может иметь большое значение, вот как я могу асинхронно конвертировать [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]
}

Edit:

Чтобы привести этот ответ в соответствие с ответом @ PhilippeC RxSwift, вот краткое изложение того, что делает каждый оператор ReactiveSwift:

  • SignalProducer.init(values:) является эквивалентом Observable.from RxSwift. Он создает производителя, который отправляет каждое значение последовательности как отдельное событие.
  • collect является эквивалентом toArray RxSwift. Он собирает каждое значение от источника-источника и отправляет их вместе в одном массиве после завершения работы источника-источника.
  • flatMap запускает продюсера convertAToB для каждого входящего A и объединяет результат в соответствии с указанным FlattenStrategy. В этом случае я использовал .concat, что делает его эквивалентным RxSwift concatMap, который объединяет каждый результат и сохраняет порядок, как @PhilippeC описывает в своем ответе.
...