客户端缓存
Apollo iOS 支持客户端缓存 GraphQL 响应数据。利用我们的缓存机制,您的应用可以使用之前已获取的本地缓存数据进行 GraphQL 请求。这有助于减少网络流量,带来以下好处:
- 更短的加载时间
- 减少服务器负载和成本
- 减少您的应用用户的流量消耗
Apollo iOS 使用正常的缓存,当配置正确时,作为您 图 的准确信息来源,使您的应用能在获取变化时做出反应。
Apollo iOS 库包含一个短暂存在的内存缓存和一个 SQLite 缓存,后者将缓存数据持久化到磁盘。
了解如何使用缓存策略来配置 GraphQL 操作 如何与缓存数据交互,请参阅我们的文档关于 获取本地缓存数据.
什么是标准化缓存?
在一个 GraphQL 客户端 中,标准化缓存将您每个 GraphQL 操作 响应分解成包含的各个独立对象。然后,每个对象根据其 缓存键 作为独立条目进行缓存。这意味着如果多个响应包含相同的对象,该对象可以合并为一个缓存条目。这有助于减少缓存整体大小,并有助于保持缓存数据的一致性和新鲜度。
由于标准化缓存在所有操作中更新缓存条目,因此一个操作获取的数据可以更新另一个操作获取的对象。这使您能够 监控查询 并且在整个应用程序中响应当前获取的变化。您可以使用此功能自动更新您的 UI 或在数据准备就绪时触发其他事件。
标准化响应
为了保持缓存规范化, Apollo iOS 处理您的 GraphQL 操作的响应数据,识别每个对象,并为新缓存条目创建或合并数据到现有缓存条目。
为了理解 Apollo iOS 如何这样做,考虑以下示例 查询:
query GetFavoriteBook {favoriteBook { # Book objectidtitleauthor { # Author objectidname}}}
在favoriteBook
字段中返回一个Book
对象,它反过来又包含一个Author
对象。来自 GraphQL 服务器的示例响应可能如下所示:
{"favoriteBook": {"id": "bk123","title": "Les Guerriers du silence","author": {"id": "au456","name": "Pierre Bordage"}}}
标准化缓存不会直接存储这个响应。相反,它会将其拆分成单独的缓存条目。默认情况下,这些缓存条目通过它们从根操作的路径来识别。
"QUERY_ROOT": {"favoriteBook": "-> #QUERY_ROOT.favoriteBook"}"QUERY_ROOT.favoriteBook": {"id": "bk123","title": "Les guerriers du silence","author": "-> #QUERY_ROOT.favoriteBook.author"}"QUERY_ROOT.favoriteBook.author": {"id": "au456","name": "Pierre Bordage"}
如果你已经缓存了至少一个查询的结果,那么QUERY_ROOT
条目总是存在的。这个条目包含了你在任何查询中包含的每个顶级字段的引用(例如,favoriteBook
)。
具有aversauthor
字段的favoriteBook
条目包含一个字符串"-> #QUERY_ROOT.favoriteBook.author"
的引用。其中-> #
表示这是一个指向另一个缓存条目的引用,在本例中是QUERY_ROOT.favoriteBook.author
条目。
根据响应路径对对象进行标准化允许我们将其他操作沿相同响应路径的改变合并。
例如,如果我们定义了另一个查询,该查询从favoriteBook
对象中获取额外的字段,它们可以合并到现有的缓存条目中。
query FavoriteBookYear {favoriteBook { # Book objectidyearPublished}}
{"favoriteBook": {"id": "bk123","yearPublished": 1993}}
在将此响应合并到缓存后,favoriteBook
条目会添加包含现有数据的yearPublished
字段。
"QUERY_ROOT.favoriteBook": {"id": "bk123","title": "Les guerriers du silence","author": "-> #QUERY_ROOT.favoriteBook.author","yearPublished": 1993}
现在favoriteBook
字段可以查询其title
和yearPublished
在一个新的query中,并且标准化的缓存可以立即从本地缓存返回响应,无需将查询发送到服务器。
query FavoriteBookTitleAndYear {favoriteBook { # Book objecttitleyearPublished}}
根据缓存键标准化对象
本部分解释了如何使用缓存键在标准化缓存中合并对象数据。有关如何配置你的缓存键的信息,请参阅自定义缓存键。
通过响应路径对响应数据进行标准化有助于我们去重相同字段的响应,但它不允许我们将来自不同字段且返回相同对象的缓存条目合并。
在此query中,我们使用路径为bestFriend.favoriteBook
的field获取一个Book
对象。
query BestFriendsFavoriteBook {bestFriend {favoriteBook { # Book objectidtitlegenre}}}
{"bestFriend" {"favoriteBook": {"id": "bk123","title": "Les guerriers du silence","genre": "SCIENCE_FICTION"}}}
当这个响应合并到缓存中时,我们为 QUERY_ROOT.bestFriend
和 QUERY_ROOT.bestFriend.favoriteBook
添加了新的缓存条目。
响应告诉我们,我们的 bestFriend
和我们有相同的 favoriteBook
!然而,同一本书的数据在我们的缓存条目中没有被去重。
"QUERY_ROOT.favoriteBook": {"id": "bk123","title": "Les guerriers du silence","author": "-> #QUERY_ROOT.favoriteBook.author","yearPublished": 1993}"QUERY_ROOT.bestFriend": {"favoriteBook": "-> #QUERY_ROOT.bestFriend.favoriteBook"}"QUERY_ROOT.bestFriend.favoriteBook": {"id": "bk123","title": "Les guerriers du silence","genre": "SCIENCE_FICTION"}
如果我们尝试获取具有 字段 favoriteBook.genre 的查询,缓存将找不到缓存条目 QUERY_ROOT.favoriteBook 上的 字段,因此它会将查询发送到服务器以获取重复数据。
为了去重不同 对象类型 返回的相同对象的数据,我们需要配置缓存以便它能够识别它们是相同的对象。我们可以通过为 Book
对象提供缓存键配置来实现这一点。
在这个示例中,Book
对象类型有一个 id
字段,该字段可以唯一地标识它。由于我们的 favoriteBook
和 bestFriend.favoriteBook
缓存条目具有相同的 id
,我们知道它们代表相同的 Book
对象。我们可以配置缓存,使用 id
字段作为所有 Book
对象的缓存 ID。这将确保缓存正确地标准化我们的缓存条目。
要配置缓存键,我们从 SchemaConfiguration.cacheKeyInfo(for type:,object:)
函数返回一个新的 CacheKeyInfo
值。
static func cacheKeyInfo(for type: Object, object: JSONObject) -> CacheKeyInfo? {switch type {case MySchema.Objects.Book:return try? CacheKeyInfo(jsonValue: object["id"])default: return nil}}
使用此设置,每次正常化的缓存为 Book
对象写入响应数据时,它将使用 id
来构建缓存键,而不是响应路径。
为了防止不同的 对象类型 之间的缓存键冲突,缓存会在提供的缓存 ID 前面加上对象的 __typename
,后面跟着冒号 (:
)。
这意味着我们的 Book
的缓存键现在是 "Book:bk123"
。
有关使用 CacheKeyInfo
配置缓存键的更多信息,请参阅 自定义缓存键。
配置 Book
类型的缓存键解析后,上述查询的响应数据将创建一个单一的、规范化的 Book
对象。
"QUERY_ROOT": {"favoriteBook": "-> #Book:bk123"}"BOOK:bk123": {"id": "bk123","title": "Les guerriers du silence","author": "-> #QUERY_ROOT.favoriteBook.author","yearPublished": 1993,"genre": "SCIENCE_FICTION"}"QUERY_ROOT.bestFriend": {"favoriteBook": "-> #Book:bk123"}
缓存项 BOOK:bk123
包含了从所有查询中从 字段 获取的所有 Book
。
favoriteBook
和 bestFriend.favoriteBook
字段 是指向具有缓存键 BOOK:bk123
的条目的缓存引用。有关更多信息关于 规范化 过程,请参阅我们的博客文章:
清除缓存数据
可以调用 clear(callbackQueue:completion:)
在您的 ApolloStore
中彻底清除所有缓存。
如果您需要更直接地与缓存交互,请查看 直接缓存访问。