我正在从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
的常见情况,但它对我来说仍然有些模糊。
非常感谢你!
答案 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 常量。
我想我个人会对第一种方法略有偏好。