加入我们,于10月8日至10日在纽约市,了解关于 GraphQL 联盟和 API 平台工程的最新技巧、趋势和新闻。加入我们,参加2024年在纽约市的 GraphQL 会议
文档
免费开始

12. 定义额外的突变


在本节中,您将实现额外的用于预订和取消行程。

添加BookTrip突变

沙盒,点击图标打开模式标签页,选择mutations,查看bookTrips突变

The docs for book trips

点击右侧的播放按钮,在探索器中打开此突变。点击加号按钮添加bookTrips突变

The book trips operation immediately after adding it

您可以在左侧侧边栏中看到,该操作包含一个数组ID参数(已添加为$bookTripsLaunchIds),操作返回的对象有三个属性:

  • 一个表示预订是否成功的success布尔值
  • 一个用于向用户显示的message字符串
  • 当前用户已预订的launches列表

点击successmessage旁边的加号,将它们添加到您的操作中。

在沙盒探索器的“”部分,添加一个标识符数组。在本例中,我们将使用单个标识符预订一次行程:

(沙箱资源管理器)
{"bookTripsLaunchIds": ["25"]}

接下来,在单词 "Variables" 的旁边,您会看到单词 "Headers"。点击它以打开头部信息部分。点击 "New Header" 按钮,然后在头部键文本框中输入 "Authorization",并将上一部分得到的令牌粘贴到值中:

The headers section

现在,单击 按钮,运行您的授权 。您将得到有关刚刚预订的行程(在本例中,为行程)的信息。

注意:如果您收到错误消息 "无法读取null的属性 'id'",这意味着根据您传递的令牌找不到用户。请确保您的授权头部格式正确且您已实际登录!

Explorer showing the result of booking a trip with an array of IDs

通过这样编写的 mutation,您可以同时预订您想要预订的任何数量的行程。然而,我们的应用程序的预订机制将只允许您一次预订一个行程。

幸运的是,有一种简单的方法可以更新 mutation 以仅需要一个单个对象。首先,将资源管理器中的 operation 名称更新为单数 BookTrip。接下来,将 mutation 更新为接收单个 $id,然后传递一个包含该 $id 的数组到 bookTrips mutation

(沙箱资源管理器)
mutation BookTrip($id:ID!) {
bookTrips(launchIds:[$id]) {
success
message
}
}

这很有帮助,因为Swift代码生成现在将生成一个仅接受单个ID的方法,而不是数组,但您仍然会在底层调用相同的mutation,而无需后端做出任何更改。

在Sandbox Explorer的Variables部分,更新JSON字典以使用id作为键,并从标识符周围删除数组括号:

(沙箱资源管理器)
{"id": "25"}

点击提交操作按钮以运行您更新的查询。返回的响应应与您之前收到的响应相同:

The result of booking a trip with a single identifier

现在您已经详细说明了操作,是时候将其放入应用程序中。转到文件 > 新建 > 文件... > 空白,将此文件命名为BookTrip.graphql,并将其添加到您其他文件旁边。将Sandbox Explorer中的最终查询粘贴进去。

BookTrip.graphql
mutation BookTrip($id: ID!) {
bookTrips(launchIds: [$id]) {
success
message
}
}

现在在终端中运行代码生成以生成新的mutation代码。

实现bookTrip逻辑

现在您拥有了BookTrip mutation,是时候实现应用程序中预订旅行的逻辑了。首先,转到DetailViewModel.swift并找到bookTrip()方法。

用以下代码替换现有的函数

DetailViewModel.swift
private func bookTrip(with id: RocketReserverAPI.ID) {
Network.shared.apollo.perform(mutation: BookTripMutation(id: id)) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success(let graphQLResult):
if let bookingResult = graphQLResult.data?.bookTrips {
if bookingResult.success {
self.appAlert = .basic(title: "Success!",
message: bookingResult.message ?? "Trip booked successfully")
self.loadLaunchDetails()
} else {
self.appAlert = .basic(title: "Could not book trip",
message: bookingResult.message ?? "Unknown failure")
}
}
if let errors = graphQLResult.errors {
self.appAlert = .errors(errors: errors)
}
case .failure(let error):
self.appAlert = .errors(errors: [error])
}
}
}

现在您已经有了预订旅行的代码。在您运行它之前,让我们添加取消行程的代码。

添加CancelTrip mutation

取消行程CancelTrip的流程与BookTrip类似。返回到沙盒的“模式”标签页,选择,然后查看cancelTrip突变的文档:

Documentation for the cancel trip mutation

点击右侧的播放按钮在探索器中打开此操作,为新的操作在探索器中添加新标签页,然后点击加号创建您的操作。

再次勾选successmessage,将它们添加到您想与取消信息一起获取的属性列表中。

同样,探索器在这里有点冗长,所以更新您的操作的名称和一个,使其更短:

(沙箱资源管理器)
mutation CancelTrip($id: ID!) {
cancelTrip(launchId: $id) {
success
message
}
}

bookTrips的一个重要区别是,您只能一次取消一个行程,因为只能接受一个ID作为参数。

在沙盒探索器的“变量”部分中,您可以使用与BookTrip相同的JSON(因为它也使用了一个名为“id”的单个标识符):

(GraphiQL)
{"id": "25"}

确保在“头部”部分中,您再次添加您的授权令牌(添加到BookTrip的标签页上的令牌将不会传递到这个新标签页):

The headers section

点击“提交操作”按钮以取消行程,您应该看到一个成功的请求:

Successful cancel trip request

再次回到Xcode,创建一个新空文件,命名为CancelTrip.graphql,并将其添加到您其他GraphQL文件旁边。然后,粘贴从沙盒探索器中的最终查询

CancelTrip.graphql
mutation CancelTrip($launchId: ID!) {
cancelTrip(launchId: $launchId) {
success
message
}
}

现在在终端中运行代码生成以生成新的mutation代码。

实现cancelTrip逻辑

现在让我们来实现应用中取消行程的逻辑,回到DetailViewModel.swift,找到cancelTrip()方法,并用以下代码替换它:

DetailViewModel.swift
private func cancelTrip(with id: RocketReserverAPI.ID) {
Network.shared.apollo.perform(mutation: CancelTripMutation(launchId: id)) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success(let graphQLResult):
if let cancelResult = graphQLResult.data?.cancelTrip {
if cancelResult.success {
self.appAlert = .basic(title: "Trip cancelled",
message: cancelResult.message ?? "Your trip has been officially cancelled")
self.loadLaunchDetails()
} else {
self.appAlert = .basic(title: "Could not cancel trip",
message: cancelResult.message ?? "Unknown failure.")
}
}
if let errors = graphQLResult.errors {
self.appAlert = .errors(errors: errors)
}
case .failure(let error):
self.appAlert = .errors(errors: [error])
}
}
}

我们还需要做的一件事是更新bookOrCancel()方法,以便真正调用我们的bookTrip(...)cancelTrip(...)方法,将TODO替换为以下代码:

DetailViewModel.swift
guard let launch = launch else {
return
}
launch.isBooked ? cancelTrip(with: launch.id) : bookTrip(with: launch.id)

现在构建并运行应用程序,如果您转到任何的详细视图并点击“预定行程”,应该会看到一个消息表示行程已成功预定,但您会发现,即使您退出详细视图并再次进入,UI也不会更新。

为什么会这样?因为您在本地缓存的行程仍然有旧的isBooked值。

有几种方法可以改变这一点,但到目前为止我们只关注一个需要您最少代码更改的方法:从网络重新查询预定信息。

从网络中强制获取数据

ApolloClient 的fetch方法为大多数参数提供了默认值,因此如果您使用默认配置,您需要提供的唯一值就是Query

但是,需要注意的是一个重要的参数是cachePolicy。默认情况下,它的值是returnCacheDataElseFetch,它基本上是按照标签上的描述执行的:它在当前缓存(默认为内存缓存)中查找数据,如果不存在,则从网络中获取。

如果数据存在的,默认的行为是返回本地副本以防止不必要的网络获取。然而,这种情况有时并不是我们想要的(尤其是在执行mutation之后)。

几种不同的缓存策略可供选择,但要绝对强制从网络刷新并更新缓存,最简单的方法是使用fetchIgnoringCacheData。这种策略在访问网络时绕过缓存,但也会将获取的结果存储在缓存中以供将来使用。

首先,我们需要将以下import添加到DetailViewModel中:

DetailViewModel.swift
import Apollo

更新loadLaunchDetails方法以接受一个参数来确定是否应该强制重新加载。如果应该强制重新加载,则将缓存策略从默认的.returnCacheDataElseFetch(存在时会从缓存返回数据)更新为.fetchIgnoringCacheData

DetailViewModel.swift
func loadLaunchDetails(forceReload: Bool = false) {
guard forceReload || launchID != launch?.id else {
return
}
let cachePolicy: CachePolicy = forceReload ? .fetchIgnoringCacheData : .returnCacheDataElseFetch
Network.shared.apollo.fetch(query: LaunchDetailsQuery(launchId: launchID), cachePolicy: cachePolicy) { [weak self] result in
// Network handling remains the same
}
}

接下来,更新bookTrip(...)cancelTrip(...)方法以使用更新的loadLaunchDetails(...)调用:

DetailViewModel.swift
// bookTrip()
self.appAlert = .basic(title: "Success!",
message: bookingResult.message ?? "Trip booked successfully")
self.loadLaunchDetails(forceReload: true)
// cancelTrip()
self.appAlert = .basic(title: "Trip cancelled",
message: cancelResult.message ?? "Your trip has been officially cancelled")
self.loadLaunchDetails(forceReload: true)

测试mutations

运行应用程序。当您预订或取消行程时,应用程序将获取更新的状态并使用正确的状态更新UI。当您出去再回来时,缓存将更新为最新状态,并显示最新状态。

在下一节中,您将学习如何使用订阅一起使用。

上一页
11. 验证您的操作
下一页
13. 编写您的第一个订阅
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,以Apollo GraphQL的名义。

隐私策略

公司