RxSwift:延迟可观察到另一个可观察完成?

RxSwift: delay observable until another observable is finished?

我从服务器加载帖子,并希望在他们的图片全部加载显示他们。 这是我尝试做的事情:

postsRepository
    .fetchPosts()
    .flatMap { posts -> Observable<[Post]> in
        return Observable.merge(posts.map({ /* Code that returns Observable<UIImage> */ }))
            .takeLast(1)
            .map { _ in posts }
    }

但是,上面的代码加载了两次图像:一次是获取所有帖子,一次是我将帖子绑定到 table 视图。怎么处理?

这很有趣...

let postsWithImages = postsRepository
    .fetchPosts()
    .flatMap { posts in
        Observable.combineLatest(
            posts
                .map { [=10=].imageURL }
                .map { URLSession.shared.rx.data(request: URLRequest(url: [=10=]))
                    .map { UIImage(data: [=10=]) }
                    .catchErrorJustReturn(nil)
                }
            )
            .map { zip(posts, [=10=]) }
    }
    .map { [=10=].map { (post: [=10=].0, image: [=10=].1) } }

如果您习惯使用 Zip2Sequence,那么最后一个 map 可能不是绝对必要的。

上面假设 Post 有一个 imageURL: URL 属性.


让我们稍微清理一下:

let postsWithImages = postsRepository
    .fetchPosts()
    .flatMap { posts in
        Observable.combineLatest(
            posts
                .map { [=11=].imageURL }
                .map { URLSession.shared.rx.image(for: [=11=]) }
            )
            .map { zip(posts, [=11=]) }
    }
    .map { [=11=].map { (post: [=11=].0, image: [=11=].1) } }

以上需要一个新函数:

extension Reactive where Base == URLSession {
    func image(for url: URL) -> Observable<UIImage?> {
        return data(request: URLRequest(url: url))
            .map { UIImage(data: [=12=]) }
            .catchErrorJustReturn(nil)
    }
}

我想我会添加一些解释。正如我在文章 Recipes for Combining Observables in RxSwift 中提到的,这里的魔法是使用 combineLatest[Observable<T>] 转换为 Observable<[T]>

运算符获取 Observable 数组,订阅所有这些,等待它们全部完成,然后发出一个事件,其中包含收集到的所有值的数组。 (之后它会在每次更新其中一项时发出一个新数组,但这与此处无关。)