类型条件
关于GraphQL 类型系统 包含接口和联合作为抽象类型,对象类型 可遵守。一个带有抽象返回类型的 GraphQL 模式 可返回任何可转换为抽象类型的具体类型。type 可转换为抽象类型。
考虑以下架构
type Query {hero: Character!}interface Character {name: String!}type Human implements Character {name: String!}type Droid implements Character {name: String!primaryFunction: String!}
由于Human和Droid类型都实现了Character接口,因此它们都可以转换为抽象类型Character。这意味着Character类型的字段(例如,hero),可以是Human或Droid。当我们查询hero字段时,服务器不会返回Character类型的响应对象,而是返回实现了Character接口的具体类型。在这种情况下,它可以是Human或Droid。
类型转换
为了知道响应中对象的类型是否可以转换为另一个类型,我们必须知道对象的实际类型。我们可以请服务器提供对象的实际类型通过获取__typename元数据字段。
Apollo iOS自动扩展您的查询,将__typename字段添加到您操作中的每一个对象。这主要是为了支持条件类型转换,但也意味着__typename属性始终定义,并在需要时可以用来手动区分对象类型。
根据以下规则,可以基于以下规则将对象转换为另一种类型,其中“目标类型”是我们试图转换为的类型:
- 如果目标类型是一个具体的对象类型:
- 如果对象类型与目标类型完全相同。
- 如果目标类型是接口:
- 如果对象类型实现了目标接口类型。
- 如果目标类型是联合体:
- 如果目标联合体类型的可能集合包括对象类型。
使用类型条件进行查询
任何时候我们想要查询类型特定的字段在具有抽象类型的对象上,我们必须使用类型条件。
在这个示例中,我们想要查询 hero 字段上的 primaryFunction,但是只有当返回的 hero 是 Droid 类型时,我们才能查询 primaryFunction 字段。下面的查询定义是无效的且无法编译:
query HeroAsDroid {hero {nameprimaryFunction # Field "primaryFunction" does not exist on type "Character"}}
我们可以使用在 Droid 类型上的 片段 来查询 primaryFunction 字段。这可以是 命名片段 或 内联片段。
query HeroAsDroid {hero {name... on Droid {primaryFunction}}}
query HeroAsDroid {hero {name...DroidDetails}}fragment DroidDetails on Droid {nameprimaryFunction}
访问条件响应数据
使用另一个类型的片段会在生成的模型对象中创建一个类型条件。
为了访问 Hero 模型中的 primaryFunction,我们需要知道英雄是否是机器人。Apollo iOS 将生成一个包含可选项 asDroid 属性的 Hero 模型:
class HeroAsDroidQuery: GraphQLQuery {struct Data: SelectionSet {let hero: Herostruct Hero: SelectionSet {let name: Stringvar asDroid: AsDroid? { ... }// Type Condition Declarationstruct AsDroid: InlineFragment {let name: Stringlet primaryFunction: String}}}}
属性 hero.asDroid 是可选的,并且只有在响应数据的英雄对象类型为机器人时才会返回值。现在我们可以有条件地将 Hero 转换为 AsDroid 模型并访问 primaryFunction 属性。
let primaryFunction: String? = hero.asDroid?.primaryFunction
转换为条件命名片段
如果类型条件是由命名片段创建的,asDroid 对象也可以用来执行 片段转换。
在这里,我们使用一个名为 DroidDetails 的片段声明类型条件:
query HeroAsDroid {hero {name...DroidDetails}}fragment DroidDetails on Droid {nameprimaryFunction}
class HeroAsDroidQuery: GraphQLQuery {struct Data: SelectionSet {let hero: Herostruct Hero: SelectionSet {let name: Stringvar asDroid: AsDroid? { ... }// Type Condition Declarationstruct AsDroid: InlineFragment {let name: Stringlet primaryFunction: String// Fragment Conversion Declarationvar fragments: Fragmentsstruct Fragments {var droidDetails: DroidDetails { ... }}}}}}
为了将 Hero 转换为 DroidDetails 片段,我们需要知道英雄是否是机器人。使用命名片段,Apollo iOS 生成一个包含转换为 DroidDetails 的 Hero.AsDroid 模型。现在我们可以将可选项转换为 DroidDetails 片段。
let droidDetails: DroidDetails? = hero.asDroid?.fragments.droidDetails
了解更多关于如何使用命名 片段 以在我们的 片段 文档。