Using fragments


In GraphQL, fragments define pieces of data you may want to reuse in multiple places:

GraphQL
1query HeroAndFriends($episode: Episode) {
2  hero(episode: $episode) {
3    name
4    ...HeroDetails
5    friends {
6      ...HeroDetails
7    }
8  }
9}
10
11fragment HeroDetails on Character {
12  name
13  appearsIn
14}

Apollo iOS generates separate result types for fragments, which means they are a great way of keeping UI components or utility functions independent of specific queries.

One common pattern is to define a fragment for a child view (like a UITableViewCell), and include the fragment in a query defined at a parent level (like a UITableViewController). This way, the child view can easily be reused and only depends on the specific data it needs:

Swift
1func configure(with heroDetails: HeroDetails?) {
2  textLabel?.text = heroDetails?.name
3}

This also works the other way around. The parent view controller only has to know the fragment name, but doesn't need to know anything about the fields it specifies. You can make changes to the fragment definition without affecting the parent.

In fact, this is the main reason fields included through fragments are not exposed directly, but require you to access the data through the fragment explicitly:

Swift
1apollo.fetch(query: HeroAndFriendsQuery(episode: .empire)) { result in
2  guard let data = try? result.get().data else { return }
3  print(data.hero?.name) // Luke Skywalker
4  print(data.hero?.appearsIn) // WON'T WORK
5  print(data.hero?.fragments.heroDetails.appearsIn) // [.newhope, .empire, .jedi]
6  print(data.hero?.friends?.flatMap { $0?.fragments.heroDetails.name }.joined(separator: ", ")) // Han Solo, Leia Organa, C-3PO, R2-D2
7}

In most cases, you'll simply pass the whole fragment to a child view without needing to know anything about the data it specifies:

Swift
1cell.configure(with: hero?.fragments.heroDetails)

Type conditions

The GraphQL type system includes interfaces and unions as abstract types that object types can conform to. In the Star Wars example schema for example, both Humans and Droids implement the Character interface. If we query for a hero, the result can be either a human or a droid, and if we want to access any type-specific properties we will have to use a fragment with a type condition:

GraphQL
1query HeroAndFriends($episode: Episode) {
2  hero(episode: $episode) {
3    name
4    ...DroidDetails
5  }
6}
7
8fragment DroidDetails on Droid {
9  name
10  primaryFunction
11}

You can access named fragments with type conditions the same way you access other fragments, but their type will be optional to reflect the fact that their fields will only be available if the object type matches:

Swift
1apollo.fetch(query: HeroAndFriendsQuery(episode: .empire)) { result in
2  guard let data = try? result.get().data else { return }
3  data.hero?.fragments.droidDetails?.primaryFunction
4}

Alternatively, you can use inline fragments with type conditions to query for type-specific fields:

GraphQL
1query HeroAndFriends($episode: Episode) {
2  hero(episode: $episode) {
3    name
4    ... on Droid {
5      primaryFunction
6    }
7  }
8}

And results from inline fragments with type conditions will be made available through specially generated as<Type> properties:

Swift
1apollo.fetch(query: HeroAndFriendsQuery(episode: .empire)) { result in
2  guard let data = try? result.get().data else { return }
3  data.hero?.asDroid?.primaryFunction
4}

You can also use inline fragments inside named fragments:

GraphQL
1query HeroAndFriends($episode: Episode) {
2  hero(episode: $episode) {
3    name
4    ...HeroDetails
5    friends {
6      ...HeroDetails
7    }
8  }
9}
10
11fragment HeroDetails on Character {
12  name
13  ... on Droid {
14    primaryFunction
15  }
16}
Swift
1apollo.fetch(query: HeroAndFriendsQuery(episode: .empire)) { result in
2  guard let data = try? result.get().data else { return }
3  data.hero?.fragments.heroDetails.asDroid?.primaryFunction
4}

Apollo iOS automatically augments your queries to add a __typename field to selection sets. This is used primarily to support conditional fragments, but it means a __typename property is always defined and can be used to differentiate between object types manually if needed.

Feedback

Edit on GitHub

Forums