Docs
Launch GraphOS Studio
You're viewing documentation for a previous version of this software. Switch to the latest stable version.

Using fragments


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

query HeroAndFriends($episode: Episode) {
hero(episode: $episode) {
name
...HeroDetails
friends {
...HeroDetails
}
}
}
fragment HeroDetails on Character {
name
appearsIn
}

generates separate result types for , 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 for a child view (like a UITableViewCell), and include the in a 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:

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

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

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

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

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

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

Type conditions

The includes interfaces and unions as abstract types that can conform to. In the Star Wars example schema for example, both Humans and Droids implement the Character interface. If we 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 with a type condition:

query HeroAndFriends($episode: Episode) {
hero(episode: $episode) {
name
...DroidDetails
}
}
fragment DroidDetails on Droid {
name
primaryFunction
}

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

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

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

query HeroAndFriends($episode: Episode) {
hero(episode: $episode) {
name
... on Droid {
primaryFunction
}
}
}

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

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

You can also use inline inside named fragments:

query HeroAndFriends($episode: Episode) {
hero(episode: $episode) {
name
...HeroDetails
friends {
...HeroDetails
}
}
}
fragment HeroDetails on Character {
name
... on Droid {
primaryFunction
}
}
apollo.fetch(query: HeroAndFriendsQuery(episode: .empire)) { result in
guard let data = try? result.get().data else { return }
data.hero?.fragments.heroDetails.asDroid?.primaryFunction
}

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

Previous
Performing mutations
Next
Client-side caching
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company