Пагинация RxSwift

Мне не удается заставить это решение работать: https://github.com/liuznsn/RxMoyaPaginationNetworking

Может кто подскажет, в чем ошибка. Переменная загрузки никогда не становится ложной. Я предполагаю, что проблема в наблюдаемом запросе, но я не могу понять, почему.

class PaginationNetworkModel<T1: Mappable>: NSObject {

let refreshTrigger = PublishSubject<Void>()
let loadNextPageTrigger = PublishSubject<Void>()
let loading = Variable<Bool>(false)
let elements = Variable<[T1]>([])
var offset:Int = 0
let error = PublishSubject<Swift.Error>()

private let disposeBag = DisposeBag()

override init() {
    super.init()

    let refreshRequest = loading.asObservable()
        .sample(refreshTrigger)
        .flatMap { [unowned self] loading -> Observable<[T1]> in
            if loading {
                return Observable.empty()
            } else {
                return self.loadData(offset: self.offset)
            }
    }

    let nextPageRequest = loading.asObservable()
        .sample(loadNextPageTrigger)
        .flatMap { [unowned self] loading -> Observable<[T1]> in
            if loading {
                return Observable.empty()
            } else {
                self.offset += 1
                return self.loadData(offset: self.offset)
            }
    }

    let request = Observable
        .of(refreshRequest, nextPageRequest)
        .merge()
        .shareReplay(1)

    let response = request.flatMap { events -> Observable<[T1]> in
        request
            .do(onError: { error in
                self.error.onNext(error)
            }).catchError({ error -> Observable<[T1]> in
                Observable.empty()
            })
    }.shareReplay(1)

    Observable
        .combineLatest(request, response, elements.asObservable()) { [unowned self] request, response, elements in
            return self.offset == 0 ? response : elements + response
        }
        .sample(response)
        .bind(to: elements)
        .addDisposableTo(rx_disposeBag)

    Observable
        .of(request.map { _ in true },
            response.map { $0.count == 0 },
            error.map { _ in false }
        )
        .merge()
        .bind(to: loading)
        .addDisposableTo(rx_disposeBag)
}

func loadData(offset: Int) -> Observable<[T1]> {
    return Observable.empty()
}

person pluck    schedule 15.04.2017    source источник
comment
Вы понимаете, что ваша loadData функция никогда не возвращает никаких данных, а это означает, что response никогда не срабатывает?   -  person Daniel T.    schedule 16.04.2017
comment
Ага. Как вы могли видеть, это общий класс, и если мне нужен другой метод сервера, я наследую этот класс. Я уверен, что ответ сработает, т.е. если я установлю для request.map значение false, я получу все объекты с сервера, но без функции загрузки.   -  person pluck    schedule 17.04.2017
comment
По какой-то причине request.map вызывается после response.map, где устанавливается false, и сразу устанавливается значение true coz запроса, но я не вижу, где он вызывается после ответа   -  person pluck    schedule 17.04.2017


Ответы (2)


Проблема здесь:

    let refreshRequest: Observable<[T1]> = loading.asObservable()
        .sample(refreshTrigger)
        .flatMap { [unowned self] loading -> Observable<[T1]> in
            if loading {
                return Observable.empty()
            } else {
                return self.loadData(offset: self.offset)
            }
    }

refreshRequest не выдает значение до после возврата loadData. То, как структурирован ваш код, испускает сигнал на refreshTrigger, запускает сетевой запрос, а затем устанавливает для загрузки значение true после завершения сетевого запроса.

Было бы лучше, если бы refreshRequest и nextPageRequest возвращали Observable о том, какую страницу загружать, а затем объединяли их и вызывали flatMap с сетевым вызовом для объединенного результата.

person Daniel T.    schedule 17.04.2017

Спасибо Даниил за помощь, вот готовое решение

Пример вызова:

goodsModel = GoodsNetworkModel()
    goodsModel.elements.asObservable().bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: StoreCell.self)) { (ip, item: Goods, cell: StoreCell) in
        cell.configure(goods: item)
    }.addDisposableTo(rx_disposeBag)

    tableView.rx.itemSelected.bind() { [unowned self] ip in
        self.didSelectRow(ip: ip.row)
    }.addDisposableTo(rx_disposeBag)

    rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
        .map { _ in () }
        .bind(to: goodsModel.refreshTrigger)
        .addDisposableTo(rx_disposeBag)

    tableView.rx_reachedBottom
        .map{ _ in ()}
        .bind(to: goodsModel.loadNextPageTrigger)
        .addDisposableTo(rx_disposeBag)

Код модели:

class PaginationNetworkModel<T1: Mappable>: NSObject {

let refreshTrigger = PublishSubject<Void>()
let loadNextPageTrigger = PublishSubject<Void>()
let loading = Variable<Bool>(false)
let elements = Variable<[T1]>([])
var offset:Int = 0
let error = PublishSubject<Swift.Error>()

private let disposeBag = DisposeBag()

override init() {
    super.init()

    let refreshRequest = loading.asObservable()
        .sample(refreshTrigger)
        .flatMap { loading -> Observable<Int> in
            if loading {
                return Observable.empty()
            } else {
                return Observable<Int>.create { observer in
                    observer.onNext(0)
                    observer.onCompleted()
                    return Disposables.create()
                }
            }
    }

    let nextPageRequest = loading.asObservable()
        .sample(loadNextPageTrigger)
        .flatMap { [unowned self] loading -> Observable<Int> in
            if loading {
                return Observable.empty()
            } else {
                return Observable<Int>.create { [unowned self] observer in
                    self.offset += 1
                    observer.onNext(self.offset)
                    observer.onCompleted()
                    return Disposables.create()
                }
            }
    }

    let request = Observable
        .of(refreshRequest, nextPageRequest)
        .merge()
        .shareReplay(1)

    let response = request.flatMap { offset -> Observable<[T1]> in
        self.loadData(offset: offset)
            .do(onError: { [weak self] error in
                self?.error.onNext(error)
            }).catchError({ error -> Observable<[T1]> in
                Observable.empty()
            })
        }.shareReplay(1)

    Observable
        .combineLatest(request, response, elements.asObservable()) { [unowned self] request, response, elements in
            return self.offset == 0 ? response : elements + response
        }
        .sample(response)
        .bind(to: elements)
        .addDisposableTo(rx_disposeBag)

    Observable
        .of(request.map{_ in true},
            response.map { $0.count == 0 },
            error.map { _ in false })
        .merge()
        .bind(to: loading)
        .addDisposableTo(rx_disposeBag)
}

func loadData(offset: Int) -> Observable<[T1]> {
    return Observable.empty()
}
person pluck    schedule 18.04.2017
comment
Если я создам PaginationNetworkModel, а затем активирую его loadNextPageTrigger, модель попытается загрузить страницу 1, даже не пытаясь загрузить страницу 0. Вы уверены, что это правильное поведение? - person Daniel T.; 18.04.2017
comment
Отредактируйте свой ответ, поэтому перед вызовом loadNextPageTrigger я всегда вызываю refreshTrigger - person pluck; 18.04.2017