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

时间:2018-04-27 12:57:41

标签: ios swift reactive-programming rx-swift

我正在从API中下载书籍列表,并且每本书都要下载其封面图片。

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

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

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<[Book]>类型,而不会弄乱嵌套的observable和大量的编译时错误。我很确定这是使用flatMap的常见情况,但它对我来说仍然有些模糊。

非常感谢你!

1 个答案:

答案 0 :(得分: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 常量。

我想我个人会对第一种方法略有偏好。