Using custom response models
The GraphQLQueryPager
supports not only returning collections of a query's Data
type, but also custom response types. The custom response types can either be a collection or a single object. This is useful when you want to transform the raw response into custom models for consumption in your application.
Transforming existing responses types
The GraphQLQueryPager
class conforms to the Publisher
protocol, and provides a map
method that allows you to transform the raw response into a custom response type. When used this way, the GraphQLQueryPager
will operate over generated Data
types, but allow each Data
type to be transformed into a custom response type. This is especially useful when you want to reuse a single pager in multiple places, transforming the raw data into view models.
In this example, we take a GraphQLQueryPager
that returns a Result<PaginationOutput<MyQuery.Data, MyQuery.Data>>
, and transform the raw response into a custom response type of Result<[Person], Error>
.
// This `GraphQLQueryPager` will return a `Result` of `PaginationOutput<MyQuery.Data, MyQuery.Data>`let initialQuery = MyQuery(first: 10, after: nil)let pager = GraphQLQueryPager(client: client,initialQuery: initialQuery,extractPageInfo: { data inCursorBasedPagination.Forward(hasNext: data.values.pageInfo.hasNextPage ?? false,endCursor: data.values.pageInfo.endCursor)},pageResolver: { page, paginationDirection inswitch paginationDirection {case .next:return MyQuery(first: 10, after: page.endCursor ?? .none)case .previous:return nil}})// Elsewhere in the applicationlet cancellable = pager.map { result inswitch result {case .success((let paginationOutput, let source)):// We may or may not care about the source, but we can use it to transform the response or pass it through as needed.// In this example, we ignore the source and transform the response into a custom response type.let pages = paginationOutput.previousPages + [paginationOutput.initialPage] + paginationOutput.nextPageslet people = pages.flatMap { page indata.values.nodes.map { Person(id: $0.id, name: $0.name) }}return Result<[Person], Error>.success(people)case .failure(let error):return .failure(error)}}.sink { result in// NOTE: This is not guaranteed to run on the main thread. Dispatch to the main thread if necessary.switch result {case .success(let people):// Handle the peoplecase .failure(let error):// Handle the error}}
Creating GraphQLQueryPager
that returns custom response types
The GraphQLQueryPager
class can also be initialized with a custom type in mind. When used this way, the GraphQLQueryPager
will operate over the custom response type, and not the raw Data
type. This is useful when you want to create a pager that returns a custom response type, and you don't want to transform the raw response into a custom response type.
We provide a convenience method for transforming collection types in this manner.
In this example, we create a GraphQLQueryPager
that transform the raw response into a custom response type of Result<([Person], UpdateSource), Error>
. The transform
closure will be called on the data from each paginated query and the resulting arrays will be concatenated together.
let initialQuery = MyQuery(first: 10, after: nil)let pager = GraphQLQueryPager(client: client,initialQuery: initialQuery,extractPageInfo: { data inCursorBasedPagination.Forward(hasNext: data.values.pageInfo.hasNextPage ?? false,endCursor: data.values.pageInfo.endCursor)},pageResolver: { page, paginationDirection in// As we only want to support forward pagination, we can return `nil` for reverse paginationswitch paginationDirection {case .next:return MyQuery(first: 10, after: page.endCursor ?? .none)case .previous:return nil}},transform: { data indata.values.nodes.compactMap { node inPerson(id: node.id, name: node.name)}})
Alternatively, you can also return a single model type, such as a Result<(MyResponseModel, UpdateSource), Error>
. To do this, we use a transform
closure with three parameters, previousPages, initialPage, nextPages
:
let initialQuery = MyQuery(first: 10, after: nil)let pager = GraphQLQueryPager(client: client,initialQuery: initialQuery,extractPageInfo: { data inCursorBasedPagination.Forward(hasNext: data.values.pageInfo.hasNextPage ?? false,endCursor: data.values.pageInfo.endCursor)},pageResolver: { page, paginationDirection in// As we only want to support forward pagination, we can return `nil` for reverse paginationswitch paginationDirection {case .next:return MyQuery(first: 10, after: page.endCursor ?? .none)case .previous:return nil}},transform: { previousPages, initialPage, nextPages inlet allPages = previousPages + [initialPage] + nextPageslet people = allPages.flatMap { page indata.values.nodes.map { Person(id: $0.id, name: $0.name) }}return MyResponseModel(people: people)})