RxSwift, зависимые от цепочки загрузки, возвращающие один и тот же наблюдаемый тип - PullRequest
0 голосов
/ 27 апреля 2018

Я загружаю список книг из API, и для каждой книги я хочу загрузить обложку.

Я застрял на том, что я думаю, что это очень простая и распространенная задача в Rx. Я провел исследование, но не могу найти решение, потому что все связанные с этим вопросы касаются того, как вернуть еще один Observable; например, получите репозитории Github, а затем получите проблемы для каждого, но верните Observable<Issue>, а не измененный Observable<Repository>, в моем случае. В моем случае я хочу изменить предыдущий наблюдаемый результат (Observable<[Book]>) с результатом другого запроса, возвращающего другое наблюдаемое (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]>, не вмешиваясь во вложенные наблюдаемые объекты и миллионы ошибок времени компиляции. Я почти уверен, что это обычный сценарий использования flatMap, но он все еще неясен для меня.

Большое спасибо!

1 Ответ

0 голосов
/ 28 апреля 2018

Возможно, следует разделить структуру 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)

Вместо использования вывода типов я добавил явные спецификации типов, чтобы было более очевидно, какие элементы передаются по цепочке.

UPDATE

Если вы хотите использовать только одну Книгу структуру, вы можете использовать это:

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 константу.

Полагаю, лично я бы предпочел первый подход к двум структурам.

...