8. 分页结果
正如你可能已经注意到的,由LaunchListQuery
返回的对象是一个 LaunchConnection
,该对象包含一个 launches 列表、一个分页 cursor,以及一个布尔值来指示是否存在更多 launch。
当使用基于 cursor 的分页系统时,重要的是要记住,cursor 给你一个位置,无论在此期间是否添加了更多项目,你都可以在某个特定的位置之后获取所有结果。
在上一个部分中,你直接在 SMALL
尺寸 参数 中硬编码了 GraphQL 查询,但你也可以使用 变量程序化地定义参数。你将在这里使用它们来实现分页。
添加一个 cursor
变量
在 LaunchList.graphql
中添加一个 cursor
变量。在 GraphQL 中,变量的前缀是美元符号。
query LaunchList($cursor: String) {launches(after: $cursor) {hasMorecursorlaunches {idsitemission {namemissionPatch(size: SMALL)}}}}
现在重新运行代码生成以更新 GraphQL 代码。
您可以在沙盒资源管理器中通过使用位于 操作名称 下方的主体部分的窗格来">实验 GraphQL 变量。如果省略 $cursor
变量,服务器返回从开始的数据:
将 LaunchListViewModel
更新为使用 cursor
首先,您需要保存最近收到的 LaunchConnection
对象。
在 LaunchListViewModel.swift
文件顶部添加一个变量来保存此对象,以及一个用于最近请求的变量,在您的 launches
变量附近:
@Published var launches = [LaunchListQuery.Data.Launches.Launch]()@Published var lastConnection: LaunchListQuery.Data.Launches?@Published var activeRequest: Cancellable?@Published var appAlert: AppAlert?@Published var notificationMessage: String?
接下来,让我们更新我们的 loadMoreLaunches()
方法以使用 cursor
属性以及管理 lastConnection
和 activeRequest
属性:
private func loadMoreLaunches(from cursor: String?) {self.activeRequest = Network.shared.apollo.fetch(query: LaunchListQuery(cursor: cursor ?? .null)) { [weak self] result inguard let self = self else {return}self.activeRequest = nilswitch result {case .success(let graphQLResult):if let launchConnection = graphQLResult.data?.launches {self.lastConnection = launchConnectionself.launches.append(contentsOf: launchConnection.launches.compactMap({ $0 }))}if let errors = graphQLResult.errors {self.appAlert = .errors(errors: errors)}case .failure(let error):self.appAlert = .errors(errors: [error])}}}
现在实现 loadMoreLaunchesIfTheyExist()
方法来检查是否有任何 launches 要加载,然后尝试加载它们。用以下代码替换 TODO
:
func loadMoreLaunchesIfTheyExist() {guard let connection = self.lastConnection else {self.loadMoreLaunches(from: nil)return}guard connection.hasMore else {return}self.loadMoreLaunches(from: connection.cursor)}
更新 UI 代码
接下来,转到 LaunchListView
并更新我们的任务以调用新实施的 loadMoreLaunchesIfTheyExist()
方法:
.task {viewModel.loadMoreLaunchesIfTheyExist()}
现在更新 List
添加一个按钮以在列表末尾加载更多 launches(可选):
List {ForEach(0..<viewModel.launches.count, id: \.self) { index inLaunchRow(launch: viewModel.launches[index])}if viewModel.lastConnection?.hasMore != false {if viewModel.activeRequest == nil {Button(action: viewModel.loadMoreLaunchesIfTheyExist) {Text("Tap to load more")}} else {Text("Loading...")}}}
测试分页
构建并运行应用,当你滚动到列表底部时,你应该会看到一个显示 Tap to load more
的行:
当你点击该行时,下批 launches 将被获取并加载到列表中。如果你继续此过程,最终 Tap to load more
按钮将不再显示,因为所有的 launches 都已经被加载。
接下来,你将 完成详情视图 以允许你预订一次 launch 的座位。