分页
Apollo分页提供了方便便捷的方式与观察分页API进行交互。它提供了一种灵活强大的方式来处理分页数据,并且可以与以下进行交互:光标 和偏移量分页。它的主要特点包括:
- 观察分页数据
- 前进、后退和双向分页支持
- 多 查询 分页支持
- 支持自定义模型类型
Apollo分页提供了两个类以与分页端点进行交互GraphQLQueryPager
和 AsyncGraphQLQueryPager
。它们的API非常相似,但后者支持 async
/ await
,以便在异步上下文中使用。
Apollo分页是其自己的Swift Package,为了使用分页功能,您需要将apollo-ios-pagination SPM包添加到您的项目中,以及apollo-ios
使用 GraphQLQueryPager
类 GraphQLQueryPager
是一个简单、灵活且强大的方式来与分页数据交互。虽然它有一个标准的初始化器,但建议使用便利的初始化器,这可以简化创建 GraphQLQueryPager
实例的过程。
在此示例中,初始化了一个 GraphQLQueryPager
,它以光标分页的方式进行正向查询分页:
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}})
在此示例中,GraphQLQueryPager
实例通过一个extractPageInfo
闭包初始化,该闭包从查询结果中提取分页信息,并一个pageResolver
闭包,它提供要执行的下一次分页查询。随后,GraphQLQueryPager
实例可用来获取分页数据,并观察分页数据的变更。
每当翻页器需要加载新页面时,它将调用extractPageInfo
闭包,传入最后一次查询的结果数据。您的extractPageInfo
实现应该返回一个PaginationInfo
值,可用于查询下一页。然后翻页器调用pageResolver
,传递由extractPageInfo
闭包提供的PaginationInfo
。您的pageResolver
实现应该返回一个使用给定PaginationInfo
查询下一页的查询。
我们可以同样通过向extractPageInfo
闭包提供OffsetPagination.Forward
而不是CursorBasedPagination.Forward
来支持基于向前偏移的分页。
使用AsyncGraphQLQueryPager
类AsyncGraphQLQueryPager
与GraphQLQueryPager
类类似,但它支持async
/await
,以在异步上下文中使用。
在此示例中,初始化了一个AsyncGraphQLQueryPager
,它以基于游标的分页方式在正向方向上分页单个查询:
let initialQuery = MyQuery(first: 10, after: nil)let pager = AsyncGraphQLQueryPager(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}})
注意,其初始化方法与GraphQLQueryPager
相同,使用相同的参数。
订阅结果
类GraphQLQueryPager
和AsyncGraphQLQueryPager
可以获取数据,但调用者必须订阅结果才能接收数据。subscribe
方法提供了一个闭包,每当翻页器获取新页面数据时都会调用该闭包。subscribe
方法是一个方便的方法,它确保闭包在主线程上调用。
// Guaranteed to run on the main threadpager.subscribe { result inswitch result {case .success(let data):// Handle the datacase .failure(let error):// Handle the error}}
GraphQLQueryPager 和 AsyncGraphQLQueryPager
都是 Combine Publisher。因此,所有 Publisher 方法都可用,例如 sink、assign、map、filter 等。
// Can run on any threadpager.sink { result inswitch result {case .success(let data):// Handle the datacase .failure(let error):// Handle the error}}
数据获取
GraphQLQueryPager 类提供了几个方法来获取分页数据: fetch
、refetch
、loadNext
、loadPrevious
和 loadAll
。
fetch
:获取第一页数据。必须在调用loadNext
或loadPrevious
之前调用。提供了一个完成处理程序,允许调用者在被通知数据检索操作完成时进行操作。refetch
:取消所有正在进行的获取 操作 和 重置分页器到其初始状态。获取第一页数据。提供了一个完成处理程序,允许调用者被通知数据检索操作何时完成。loadNext
:获取下一页数据。只能在调用fetch
后调用。提供了一个完成处理程序,允许调用者在操作完成后被通知,附带一个可选的Error?
参数,其中包含可能发生的任何使用错误。loadPrevious
:获取上一页数据。只能在调用fetch
后调用。提供了一个完成处理程序,允许调用者在操作完成后被通知,附带一个可选的Error?
参数,其中包含可能发生的任何使用错误。loadAll
:获取所有页面的数据。如果没有检测到初始页面,它将首先调用fetch
来获取第一页数据。将继续获取所有页面,直到一个PageInfo
对象指示没有更多页面要获取。此函数与前向、反向和双向分页兼容。提供了一个完成处理程序,允许调用者在操作完成后被通知,附带一个可选的Error?
参数,其中包含可能发生的任何使用错误。
AsyncGraphQLQueryPager 类提供了与异步函数相同的方法,但不需要完成处理程序,因为在异步情况下不需要。
取消正在进行的请求
GraphQLQueryPager 类提供了一个 reset
方法,可用于取消所有正在进行的获取 操作 并停止监视缓存数据的更改。这不会取消任何分页器的订阅者。一旦分页器状态已重置,您可以调用 fetch
重新开始接收更新,并且现有订阅者将继续接收更新。
错误处理
GraphQLQueryPager类可以抛出两种类型的错误:由网络操作引起的错误或由使用引起的错误。当分页器遇到网络错误,如超时或连接错误时,将通过网络操作结果的
Result
传递给订阅者来暴露给用户。使用错误,例如取消或在一个加载正在进行时尝试开始一个新的获取,会被抛出为PaginationError
类型(对于AsyncGraphQLQueryPager
),或者在每种获取方法的回调中暴露(对于GraphQLQueryPager
)。请注意,GraphQLQueryPager
的回调是可选的,用户可以选择忽略它们。
GraphQLQueryPager
中的使用错误
方法loadNext
、loadPrevious
和loadAll
都有一个完成处理函数,它和一个Result
类型一起被调用。这个Result
类型可能包含分页数据或一个PaginationError
类型。常见的分页错误是在已有正在进行的加载时尝试获取数据,或在调用fetch
前尝试获取前一个或后一个页面。
// Attempting to fetch a previous page without first calling `fetch`pager.loadNext { error inif let error {// Handle error} else {// We have no error, and are finished with our fetch operation}}// Note that we can silently ignore the errorpager.loadNext()
AsyncGraphQLQueryPager
中的使用错误
类AsyncGraphQLQueryPager
可以直接抛出PaginationError
类型,而不是通过完成处理函数来暴露它。作为一个本征异步类型,AsyncGraphQLQueryPager
可以在Task
内部抛出错误并将其转发给调用者。
// Attempting to fetch a previous page without first calling `fetch`try await pager.loadNext()// Similarly, we can silently ignore the errortry? await pager.loadNext()