自定义缓存键
当与规范化缓存协同工作时,建议你为每个缓存 ID对象类型指定一个
规范化缓存为存储在缓存中的每个对象计算一个缓存键。使用Apollo iOS,你可以自定义缓存键的计算,以改进缓存的性能和功能。
有关更多信息,请参阅缓存如何通过缓存键规范化对象。
CacheKeyInfo
构造缓存键所需的信息由一个CacheKeyInfo
值表示。这个struct
包括两个属性,你可以提供它们以影响缓存键的计算方式:
let uniqueKeyGroup: String?
一组应在规范化缓存中一起组合的对象的可选分组标识符。这是缓存键的第一个组件。
重要:缓存键组的唯一性
具有相同
uniqueKeyGroup
的所有对象必须在所有类型中具有唯一的id
。为了避免缓存键冲突,缓存键将始终包含一个组标识符组件。当
uniqueKeyGroup
为nil
(默认值)时,响应对象中的__typename
用作组标识符的默认值。如果缓存中将可以合并多个不同类型的对象,则每个
Object
的CacheKeyInfo
应该具有相同的uniqueKeyGroup
。提示:通过将对象分组,它们在规范化缓存中的键将具有相同的前缀。这允许您通过缓存的
id
搜索同一组中的缓存对象。要了解更多信息,请阅读有关直接缓存访问let id: String
代表对象的唯一缓存 ID。它用作缓存键的第二部分。
重要:
ID必须对所有具有相同组标识符(
__typename
或uniqueKeyGroup
)的对象是确定性的且唯一的。也就是说,对于代表缓存中同一实体的响应对象,其键将会是相同的;并且对于代表具有相同组标识符的其他不同对象,也不会使用相同的键。
规范化缓存构建缓存键的格式
"${GroupIdentifier}:${CacheID}"
给定一个CacheKeyInfo
:
CacheKeyInfo(id: "123", uniqueKeyGroup: "Animal")
Apollo iOS会构建一个形如"Animal:123"
的缓存键。
SchemaConfiguration
文件
The SchemaConfiguration
文件是您配置模式中类型缓存键的起点。
当 Apollo iOS 为您的项目生成代码时,它会生成一组代表应用 GraphQL 语法的元数据类型。这些类型之一名为 SchemaConfiguration.swift
。
如果代码生成引擎检测到该文件不存在,则会创建此文件,但永远不会覆盖现有的 SchemaConfiguration.swift
文件。这意味着您可以在后续代码生成运行中不会覆盖方案配置的情况下编辑您的方案配置。
提示:您可以在代码生成配置中通过 output.schemaTypes
选项来配置生成的方案类型的位置。
指定缓存 ID
`SchemaConfiguration` 包含一个 cacheKeyInfo(for:type:object:)
函数。此函数为您提供 JSON 响应object
和所表示对象的具体 type
。
`object` 参数提供来自网络请求或缓存命中的 JSON 响应。
`type` 参数提供了一个强类型 Object
类型。这是一个表示 GraphQL 方案中具体 type
的生成元数据类型。
要配置如何从响应对象中计算缓存键,您可以创建并从您的 cacheKeyInfo(for:object:)
实现
注意:在指定缓存ID时,请确保您总是获取用于构建这些ID的字段来执行您的操作。任何不包含缓存ID 字段的响应对象将无法通过缓存归一化进行合并。
使用默认缓存ID字段
如果您的架构在许多对象类型之间提供了常见的唯一标识符,您可能希望将该字段作为默认的缓存ID使用。
public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {guard let id = object["id"] as? String else {return nil}return CacheKeyInfo(id: id)}}
如果JSON响应对象
没有id
字段,函数返回nil
,且缓存将使用默认的响应路径归一化来归一化对象。
JSON值便利初始化器
或者,您可以使用init(jsonValue:uniqueKeyGroup:)
便利初始化器。此初始化器尝试使用JSON响应中某个键的值,如果该键不存在则抛出错误。
如果您希望在值不存在时返回nil
,则可以使用try?
。
public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {return try? CacheKeyInfo(jsonValue: object["id"])}}
按Object
类型指定缓存ID
如果您希望为不同类型的对象指定不同的缓存ID,则可以使用type
参数。
例如,对于具有唯一键字段id
的Dog
类型对象,您可能可以实现缓存键解析如下:
public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {switch type {case Objects.Dog:return try? CacheKeyInfo(jsonValue: object["id"])default:return nil}}}
按抽象类型指定缓存ID
如果架构中具有相同interface
或union
的对象类型共享相同的缓存键解析策略,则您可以根据这些抽象类型解析键。
生成的架构元数据包括Interfaces
和Unions
类型,其中包含在您的GraphQL架构中使用的所有抽象类型列表。
例如,对于实现interface Pet
的Dog
和Cat
类型架构,您可能可以实现缓存键解析如下:
public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {if type.implements(Interfaces.Pet) {return try? CacheKeyInfo(jsonValue: object["id"])}return nil}}
要配置基于联合(union
)类型的缓存键解析,请使用联合的possibleTypes
属性。
public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {if Unions.ClassroomPets.possibleTypes.contains(type) {return try? CacheKeyInfo(jsonValue: object["id"])}return nil}}
使用uniqueKeyGroup
分组合并缓存对象
如果您的缓存ID在整个不同的类型中保证是唯一的,您可能希望在缓存中使用一个共同的uniqueKeyGroup
把它们分组在一起。
有关合并缓存对象的更多信息,请参见uniqueKeyGroup
。
例如,如果所有实现interface Animal
的对象(无论是Dog
、Cat
还是其他实现了Animal
的类型)都有唯一的缓存ID,它们可以共享一个uniqueKeyGroup
。
public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {if type.implements(Interfaces.Pet) {return try? CacheKeyInfo(jsonValue: object["id"],uniqueKeyGroupId: Interfaces.Pet.name)}return nil}}
注意事项与限制
在实现缓存键解析函数时,请注意缓存键解析有几点显著的异常和限制。
虽然对象的缓存键可以使用另一个嵌套对象的
字段,但如果在另一个操作中更改了引用对象的字段,则依赖于该对象的缓存键将不会更新。对于没有与自己的缓存键进行归一化的嵌套对象,这不会发生,但如果嵌套对象是具有自己缓存键的 实体,它可以独立地进行变异。在这种情况下,任何缓存键依赖于被变异实体的其他对象都不会自动更新。您必须注意使用缓存变异手动更新这些实体。 此函数传递给此函数的
object
表示特定模型中对象的数据,而不是模式中的类型。这意味着别名字段将按它们的别名名称进行键控,而不是模式类型上的字段名称。 此函数的
object
参数是一个ObjectData
结构,它包装了底层对象数据。由于缓存键解析在原始JSON(来自网络响应)上以及SelectionSet
模型数据(直接写入缓存时)上都会执行,底层数据将具有不同的格式。ObjectData
包装器用于在此上下文中将此数据归一化到一致格式。