Fragments
The GraphQL spec defines two types of fragments:
- Named fragments, which enable you to reuse a set of fields across multiple operations or selection sets.
- Inline fragments, which can group fields and apply type conditions or directives but are not reusable.
Both named and inline fragments can be used as type conditions, which enables you to access fields of polymorphic types.
Read the guide on type conditions to learn more.
Named fragments
A named fragment is defined in a .graphql
file, just like an operation definition. A named fragment always has a name and a "parent type", that is, the type in the schema that it can be applied to. This fragment can be included in a selection set in an operation definition.
Apollo iOS generates separate result types for named fragments, which means they are a great way of keeping UI components or utility functions independent of specific queries.
fragment HeroDetails on Character {nameappearsIn}
query HeroAndFriends {hero {name...HeroDetailsfriends {...HeroDetails}}}
Apollo iOS generates a type-safe model for the HeroDetails
fragment that looks something like this (details are omitted to focus on the class structure):
struct HeroDetails: MySchema.SelectionSet, Fragment {let name: Stringlet appearsIn: [Episode]}
The result data models generated for the HeroAndFriendsQuery
will include the fields from the HeroDetails
fragment and are able to be converted into the HeroDetails
fragment model.
class HeroAndFriendsQuery: GraphQLQuery {struct Data: SelectionSet {let hero: Herostruct Hero: SelectionSet {let name: Stringlet friends: [Friend]let appearsIn: [Episode]// Fragment Conversion Declarationvar fragments: Fragmentsstruct Fragments {var heroDetails: HeroDetails { ... }}struct Friend: SelectionSet {let name: Stringlet appearsIn: [Episode]// Fragment Conversion Declarationvar fragments: Fragmentsstruct Fragments {var heroDetails: HeroDetails { ... }}}}}}
Fragment conversion
A result model that includes a fragment in it's definition will contain a Fragments
struct that facilitates fragment conversion.
Both the Hero
and the Friend
models from the HeroAndFriendsQuery
above have a Fragments
struct and can be converted to the HeroDetails
fragment.
let hero: HeroDetails = data.hero.fragments.heroDetailslet friends: [HeroDetails] = data.hero.friends.map { $0.fragments.heroDetails }
Fragment reuse
Fragment conversion allows you to reuse fragments as common models across different operations or multiple objects within the same operation.
One common pattern is to define a fragment for a child view, and include the fragment in a query defined at a parent level. This way, the child view can easily be reused and only depends on the specific data it needs.
The HeroDetails
fragment above is used for both the Hero
and Friend
models from the HeroAndFriendsQuery
, but a hero can be displayed alongside their friends in your application by showing a list of views that each are configured with a HeroDetails
fragment model.
struct HeroDetailsListView: View {let data: HeroAndFriendsQuery.Datavar body: some View {VStack {HeroDetailsView(hero: data.hero.fragments.heroDetails)for friend in data.hero.friends {HeroDetailsView(hero: friend.fragments.heroDetails)}}}}struct HeroDetailsView: View {let hero: HeroDetailsvar body: some View {HStack {Text(hero.name)Text(hero.appearsIn.description)}}}
You can add additional data to the HeroDetails
fragment and display that data in the HeroDetailsView
without affecting the parent view at all. Additionally, the HeroDetailsView
can be used with other GraphQL operations that include the HeroDetails
fragment.
Inline fragments
An inline fragment is a fragment that has no name. It is declared "inline" within another definition and cannot be reused. Inline fragments can be included in an operation definition, a named fragment definition, or nested inside another inline fragment. They are typically used to conditionally include fields in an operation using a type conditions or the @skip
/@include
directives.
Apollo iOS does not generate individual models for inline fragments, instead they affect the generated models for the operations or fragments containing them.
You can use inline fragments with type conditions to query for type-specific fields:
query HeroAndFriends($episode: Episode) {hero(episode: $episode) {name... on Droid {primaryFunction}}}
And results from inline fragments with type conditions will be made available through specially generated as<Type>
properties:
let name: String = data.hero.namelet primaryFunction: String = data.hero.asDroid?.primaryFunction
For more information about how to use inline fragments as type conditions, see the type conditions documentation.