简体   繁体   English

如何使用 RxSwift 和 Alamofire 进行分页?

[英]How to use pagination using RxSwift and Alamofire?

I am trying to consume an api with alamofire and rxswift.我正在尝试使用 alamofire 和 rxswift 使用 api。 I have written the methods but the onNext of the observer is getting called only once.我已经编写了方法,但是观察者的onNext只被调用了一次。 I am trying to do it with recursive call.我正在尝试通过递归调用来做到这一点。 What is wrong with this?这有什么问题? Api will return 10 object at a time based on the timestamp. Api 将根据时间戳一次返回 10 个对象。 So I am checking if just returned array contains 10 objects.所以我正在检查刚刚返回的数组是否包含 10 个对象。 If yes then there are more, if not then that's the end.如果是,那么还有更多,如果不是,那就是结束。

func fetchPersonalization(fromList:[Personalization],timeStamp:Int) -> Observable<PersonalizationContainer>
    {


        let dictHeader = ["Accept":"application/json","regid" :  pushtoken , "os" : "ios" , "token" : token , "App-Version" : "1324" ,  "Content-Type" :  "application/json"]

        return fetchPersonalizationUtil(dictHeader: dictHeader, timeStamp: timeStamp)
            .flatMap { (perList) -> Observable<PersonalizationContainer> in

                    let persoList:[Personalization] = perList.list
                    let finalList = fromList + persoList
                    if(persoList.count==10){
                        let newTimeStamp = persoList.last!.lastModifiedAt! - 1

                        return Observable.merge(Observable.just(PersonalizationContainer(l: finalList, d: perList.data)),
                            self.fetchPersonalization(fromList:finalList,timeStamp: newTimeStamp)
                        )
                            //self.fetchPersonalization(fromList:finalList,timeStamp: newTimeStamp)



                    }else {
                        return Observable.just(PersonalizationContainer(l: finalList, d: Data()))
                    }

        }
    }

    func fetchPersonalizationUtil(dictHeader:[String:String],timeStamp:Int) -> Observable<PersonalizationContainer>
    {

        return Observable<PersonalizationContainer>.create({ (observer) -> Disposable in
            Alamofire.request("https://mranuran.com/api/hubs/personalization/laterthan/\(timeStamp)/limit/10/" ,headers: dictHeader).responseData { response in
                if let json = response.result.value {
                    //print("HUBs JSON: \(json)")

                    do {
                        let list = try JSONDecoder().decode([Personalization].self, from: json)
                        let pContainer = PersonalizationContainer(l: list, d: json)
                        print("ANURAN \(list[0].name)")
                        observer.onNext(pContainer)
                        observer.onCompleted()
                    }catch {
                        print(error)
                        observer.onError(error)
                    }

                }
                else{
                    observer.onError(response.result.error!)
                }
            }

            return Disposables.create()
        })

    }

I put a break point on the onNext method and it seemed it's getting called only once.我在 onNext 方法上设置了一个断点,它似乎只被调用了一次。 Stuck with this for hours and RxSwift's GithubRepo example in their official github repo, I can't figure it out what they are doing.在他们的官方 github 存储库中坚持了几个小时和 RxSwift 的 GithubRepo 示例,我无法弄清楚他们在做什么。 What can be wrong with my process?我的流程有什么问题?

I wrote this up a while back using Promises, here it is using Singles.不久前我使用 Promises 写了这篇文章,这里使用的是 Singles。

You pass in:你传入:

  1. the seed which is used to make the first network call.用于进行第一次网络调用的种子。
  2. the pred which will be given the results of the most recent call and produces either an argument to make the next network call or nil if done. pred 将给出最近一次调用的结果,并产生一个参数来进行下一次网络调用,如果完成则为 nil。 (In here is where you would check the count and return the next time stamp if another call is required.) (如果需要另一个调用,您可以在这里检查计数并返回下一个时间戳。)
  3. the producer which makes the network call.进行网络调用的生产者。

It eventually returns a Single with an array of all the results.它最终返回一个包含所有结果的数组的 Single 。 It will error if any of the internal network calls error out.如果任何内部网络调用错误,它将出错。

func accumulateWhile<T, U>(seed: U, pred: @escaping (T) -> U?, producer: @escaping (U) -> Single<T>) -> Single<[T]> {
    return Single.create { observer in
        var disposable = CompositeDisposable()
        var accumulator: [T] = []
        let lock = NSRecursiveLock()
        func loop(_ u: U) {
            let product = producer(u)
            let subDisposable = product.subscribe { event in
                lock.lock(); defer { lock.unlock() }
                switch event {
                case let .success(value):
                    accumulator += [value]
                    if let u = pred(value) {
                        loop(u)
                    }
                    else {
                        observer(.success(accumulator))
                    }
                case let .error(error):
                    observer(.error(error))
                }
            }
            _ = disposable.insert(subDisposable)
        }
        loop(seed)
        return disposable
    }
}

I don't think the lock is actually necessary, but I put it in just in case.我不认为锁实际上是必要的,但我把它放在以防万一。

I've improved, based on @Daniel T.'s answer , by adding next page loading trigger.根据@Daniel T. 的回答,我通过添加下一页加载触发器进行了改进。 This is useful when the next page should be loaded only when user scrolls to the bottom of UITableView of in similar cases.这在类似情况下仅当用户滚动到UITableView底部时才应加载下一页时很有用。

First page is loaded instantly upon subscribe and each subsequent page right after receiving a signal in nextPageTrigger parameter订阅后立即加载第一页,并在nextPageTrigger参数中收到信号后立即加载每个后续页面

Example usage:用法示例:

let contents = loadPagesLazily(
    seed: 1,
    requestProducer: { (pageNumber: Int) -> Single<ResponseContainer<[Content]>> in
        return dataSource.loadContent(page: Id, pageSize: 20)
    },
    nextKeySelector: { (responseContainer: ResponseContainer<[Content]>) -> Meta? in
        let hasMorePages = responseContainer.meta.currentPage < responseContainer.meta.lastPage

        return hasMorePages ? responseContainer.meta.currentPage + 1 : nil
    },
    nextPageTrigger: loadMoreTrigger
)

return contents
    .scan([]], accumulator: { (accumulator, nextPageContainer) -> SearchResults in
        accumulator + nextPageContainer.data
    })

Parameters:参数:

  • seed - first page loading information PageKey seed - 第一页加载信息PageKey
  • requestProducer - transforms each PageKey to a page loading Single requestProducer - 将每个PageKey转换为一个加载Single的页面
  • nextKeySelector - creates next page loaging info based on data retrieved in eah page resulted from requestProducer call. nextKeySelector - 根据从requestProducer调用产生的每个页面中检索到的数据创建下一个页面加载信息。 Return nil here if there is no next page.如果没有下一页,则在此处返回nil
  • nextPageTrigger - after receiving first page each subsequent page is returned only after receiving a .next signal in this observable/ nextPageTrigger - 接收第一页后,每个后续页仅在此 observable 中接收到.next信号后返回/
func loadPagesLazily(
    seed: PageKey,
    requestProducer: @escaping (PageKey) -> Single<Page>,
    nextKeySelector: @escaping (Page) -> PageKey?,
    nextPageTrigger: Observable<Void>
) -> Observable<Page> {
    return requestProducer(seed)
        .asObservable()
        .flatMap({ (response) -> Observable<Page>  in
            let nextPageKey = nextKeySelector(response)

            let nextPageLoader: Observable<Page> = nextPageKey
                .map { (meta) -> Observable<Page> in
                    nextPageTrigger.take(1)
                        .flatMap { (_) -> Observable<Page> in
                            loadPagesLazily(
                                seed: meta,
                                requestProducer: requestProducer,
                                nextKeySelector: nextKeySelector,
                                nextPageTrigger: nextPageTrigger
                            )
                        }
                } ?? Observable.empty()

            // Concatenate self and next page recursively
            return Observable
                .just(response)
                .concat(nextPageLoader)
        })
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM