简体   繁体   中英

RxSwift Pagination and APIs issue

I have been playing about with the PokeApi while doing some RxSwift practice. Pokeapi can be found here

https://pokeapi.co/

Example of api I am working with

While searching through pokemon, you can set a limit of how many you want returned, then in the response, there is a next and previous param so you can continue going forward and backgrounds through the pokemon. Example of part of the response

https://pokeapi.co/api/v2/pokemon/?limit=20

{"count":964,"next":" https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20 ","previous":null,"results":....

In my controller, there is text field where they can enter how many pokemon they want returned per page, and then when they hit search, results returned in table and they can hit next and previous to cycle through the pages. My Model looks like this

struct PokemonSearchData: Codable {
    let next: String?
    let previous: String?
    let results: [PokemonResultsData]
}

struct PokemonResultsData: Codable {
    let name: String
    let url: String
}

Here is part of view model showing what I am doing atm

extension PokeListViewModel: ViewModelType {
    struct Input {
        let limitText: Observable<String>
        let startRequest: PublishSubject<Void>
        let nextTap: Observable<Void>
        let previousTap: Observable<Void>
        let selectedPokemon: Observable<PokemonResultsData>
    }

    struct Output {
        let responseData: Observable<PokemonSearchData>
        let errors: Driver<Error>
    }

    func transform(input: PokeListViewModel.Input) -> PokeListViewModel.Output {
        let request = input.startRequest
            .withLatestFrom(input.limitText)
            .flatMap { text in
            self.service.fetchPokemon(limit: text).materialize()
        }.share()

        let searchResponse = request.map{$0.element}.filterNil()
        let searchError = request.map{$0.error}.asDriver(onErrorJustReturn: RxError.unknown).filterNil()

        let nextURL = searchResponse.map{$0.next}.filterNil()

        // handle next request and errors
        let nextRequest = input.nextTap.withLatestFrom(nextURL).flatMap { nextURL in
            self.service.cyclePokemon(stringURL: nextURL).materialize()
            }.share()
        let nextResponse = nextRequest.map{$0.element}.filterNil()
        let nextError = nextRequest.map{$0.error}.asDriver(onErrorJustReturn: RxError.unknown).filterNil()

        let mergedResponses = Observable.merge(searchResponse, nextResponse)
        let mergedErrors = Driver.merge(searchError, nextError)

// do the same thing for previous response, just cut it out here so less code to paste, but the logic is the same

        return Output(responseData: mergedResponses,
                      errors: mergedErrors)
    }

The next request is dependent on the initial request, so when I hit next again, it is still based on the initial request so it just keeps returning the same data. I have seen other code samples doing pagination based on the api response having page numbers, but there is no page number here, it is all based on the previous request response.

Any help would be much appreciated

There is a an RXPager pod that will do this for you. Its useful to look at the code even if you want to do you own pagination logic. The idea is basically that you need to provide the pager with 2 functions:

1) A way to generate the next page from the current page (or nil if its the first page) 2) A has next function to determine if thee current page is the last page.

And and observable trigger that you can bind to the pager (such as your tableview's contentOffset.y distance from the bottom) that will trigger the next page load (if has next is true).

I'm not sure if your problem is RxSwift related. To my understanding, you need to use the offset parameter when you make the call. For example, this request:

https://pokeapi.co/api/v2/pokemon/?limit=4

generates the following response:

{
  "count": 964,
  "next": "https://pokeapi.co/api/v2/pokemon/?offset=4&limit=4",
  "previous": null,
  "results": [
    {
      "name": "bulbasaur",
      "url": "https://pokeapi.co/api/v2/pokemon/1/"
    },
    {
      "name": "ivysaur",
      "url": "https://pokeapi.co/api/v2/pokemon/2/"
    },
    {
      "name": "venusaur",
      "url": "https://pokeapi.co/api/v2/pokemon/3/"
    },
    {
      "name": "charmander",
      "url": "https://pokeapi.co/api/v2/pokemon/4/"
    }
  ]
}

See how the value of the next key is the URL you should hit next?

"next": "https://pokeapi.co/api/v2/pokemon/?offset=4&limit=4",

I believe if you keep track of the offset value, you'll be able to receive the data you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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