RxSwift,依赖链的下载返回相同的 Observable 类型

Posted

技术标签:

【中文标题】RxSwift,依赖链的下载返回相同的 Observable 类型【英文标题】:RxSwift, chain dependent downloads returning same Observable type 【发布时间】:2018-04-27 12:57:41 【问题描述】:

我正在从 API 下载书籍列表,并且我想为每本书下载封面图片。

我坚持认为这是 Rx 中非常简单和常见的任务。我已经进行了研究,但找不到解决方案,因为所有相关问题都是关于如何返回另一个Observable;例如获取 Github 存储库,然后获取每个存储库的问题,但返回 Observable<Issue> 不是修改后的 Observable<Repository>,这就是我的情况。就我而言,我想用另一个返回不同可观察结果 (Observable<Data>) 的请求的结果来修改以前的可观察结果 (Observable<[Book]>)。

现在我有第一部分,下载书籍:

func search(query: String) -> Observable<[Book]> 
    return networkSession.rx
        .data(request: URLRequest(url: url))
        .map( try JSONDecoder().decode([Book].self, from: $0) )

Book 是一个简单的结构体:

struct Book 
    let title: String
    let authors: [String]
    let coverImageURL: URL
    var coverImage: Data?

之后,我可以下载每个图像,但我不知道如何将其分配给每个对象,以便返回相同的 Observable&lt;[Book]&gt; 类型,而不会弄乱嵌套的 observables 和无数的编译时错误。我很确定这是使用flatMap 的常见场景,但它对我来说仍然有点模糊。

非常感谢!

【问题讨论】:

【参考方案1】:

也许应该将 Book 结构拆分为两个结构。第一个在此处称为 BookInfo,并通过调用函数 search 下载它们的数组。

struct BookInfo 
    let title: String
    let authors: [String]
    let coverImageURL: URL

BookInfo 实例与 coverImage 数据组合在一起可能会导致 Book 结构如下:

struct Book 
    let bookInfo: BookInfo
    let coverImage: Data

RxSwift 链如下所示:

self.search(query: "<someQuery>") 
.flatMap  (bookInfos: [BookInfo]) -> Observable<BookInfo> in
    Observable.from(bookInfos)

.flatMap  (bookInfo: BookInfo)  -> Observable<Book> in
    //use the coverImageURL from the closure parameter and fetch the coverImage
    //return an Observable<Book>  

.observeOn(MainScheduler.instance)
.do(onNext:  (book: Book) in
    //e.g. add a book in main thread to the UI once the cover image is available
)
.subscribe()
.disposed(by: disposeBag)

我没有使用类型推断,而是添加了显式类型规范,因此更明显的是什么样的元素通过链传递。

更新

如果你只想使用一个 Book 结构,你可以使用这个:

self.search(query: "<someQuery>") 
.flatMap  (books: [Book]) -> Observable<Book> in
    Observable.from(books)

.flatMap  (book: Book)  -> Observable<Book> in
    //use the book.coverImageURL from the closure parameter and fetch the coverImage
    //since the closure parameter 'book' is a let constant you have to construct a new Book instance 
    //return an Observable<Book>     

...

请注意,闭包参数是 let 常量。这意味着您不能更改 coverImage 属性,即使它在结构中定义为 var。相反,您必须创建 Book 结构的新实例,用来自 Closure 参数的值填充它,然后添加 coverImage 数据。因此,如果您想走这条路,您还可以选择将 coverImage 更改为 let 常量。

我想我个人会稍微偏爱使用这两个结构的第一种方法。

【讨论】:

很棒的方法,我喜欢它:D 我会试一试的。在接受您的回答之前,让我等待有人回答而不使用两个结构。 很高兴你喜欢它:D。我更新了答案并在不使用两个结构的情况下添加了第二种方法。 感谢您的更新,这正是我想要的 :)

以上是关于RxSwift,依赖链的下载返回相同的 Observable 类型的主要内容,如果未能解决你的问题,请参考以下文章

来自数组的 RxSwift 可观察对象序列

RxSwift,分享+重试机制

具有相同代码库和多个工具链的 ccache

RxSwift之深入解析Using操作的应用和原理

RxSwift:使用运算符组合下载照片并保存在本地

RxSwift:返回一个带有错误的新可观察对象