Это была забавная головоломка.
«Специальный соус», который решает проблему, находится в этой строке:
.flatMap {
Observable.combineLatest($0.map {
Observable.combineLatest(
Observable.just($0.0),
URLSession.shared.rx.data(request: $0.1)
.materialize()
)
})
}
map
перед строкой создает Observable<[(URL, URLRequest)]>
, и рассматриваемая строка преобразует его в Observable<[(URL, Event<Data>)]>
.
Строка делает это следующим образом:
- Настройка сетевого вызова для создания
Observable<Data>
- Материализуйте его, чтобы создать
Observable<Event<Data>>
(это сделано для того, чтобы ошибка в одной загрузке не закрывала весь поток.)
- Поднимите URL обратно в Observable, что дает нам
Observable<URL>
- Объедините наблюдаемые из шагов 2 и 3, чтобы получить
Observable<(URL, Event<Data>)>
.
- Отобразить каждый элемент массива для получения
[Observable<(URL, Event<Data>)>]
- Объедините наблюдаемые в этом массиве, чтобы в итоге получить
Observable<[(URL, Event<Data>)]>
Вот код
// manipulatedUsers is for the code you commented out.
// users: Observable<Users>
let users = self.dataManager.getUsers()
.map(manipulatedUsers) // manipulatedUsers(_ users: Users) -> Users
.asObservable()
.share(replay: 1)
// this chain is for handling the users object. You left it blank in your code so I did too.
users
.observeOn(MainScheduler.instance)
.subscribe(onNext: { users in
})
.disposed(by: disposeBag)
// This navigates through the users structure and downloads the images.
// images: Observable<(URL, Event<Data>)>
let images = users.map { $0.categories ?? [] }
.map { $0.flatMap { $0.profiles ?? [] } }
.map { $0.compactMap { $0.thumbnail } }
.map { $0.compactMap { URL(string: $0) } }
.map { $0.map { ($0, URLRequest(url: $0)) } }
.flatMap {
Observable.combineLatest($0.map {
Observable.combineLatest(
Observable.just($0.0),
URLSession.shared.rx.data(request: $0.1)
.materialize()
)
})
}
.flatMap { Observable.from($0) }
.share(replay: 1)
// this chain filters out the errors and saves the successful downloads.
images
.filter { $0.1.element != nil }
.map { ($0.0, $0.1.element!) }
.map { ($0.0, UIImage(data: $0.1)!) }
.observeOn(MainScheduler.instance)
.bind(onNext: { url, image in
try? Disk.save(image, to: .caches, as: url.lastPathComponent)
return // need two lines here because this needs to return Void, not Void?
})
.disposed(by: disposeBag)
// this chain handles the download errors if you want to.
images
.filter { $0.1.error != nil }
.bind(onNext: { url, error in
print("failed to download \(url) because of \(error)")
})
.disposed(by: disposeBag)