类型条件
关于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
了解更多关于如何使用命名 片段 以在我们的 片段 文档。