Using Selection Set Initializers
Being able to create instances of your generated operation models can be useful in a number of different ways: adding custom data to the normalized cache; setting up fixture data for SwiftUI previews or loading states; and as an alternative to Test Mocks. Apollo iOS provides Selection Set Initializers to facilitate this.
When code generation is configured to generate selection set initializers each Swift struct will have an initializer that accepts values for the selected fields (properties) defined in the struct, as well as some inherited fields.
To learn more about how to configure Selection Set Initializers, check out Codegen Configuration.
Usage
Generated operation models are immutable and selection set initializers provide a type-safe way to create instances of your operation models.
For example, given the following generated operation model (details are omitted to focus on the relevant parts of the struct):
1public class HerosQuery: GraphQLQuery {
2 public struct Data: GraphAPI.SelectionSet {
3 ...
4
5 public var hero: Hero { __data["hero"] }
6
7 public init(
8 hero: Hero
9 ) { ... }
10
11 /// Hero
12 ///
13 /// Parent Type: `Character`
14 public struct Hero: GraphAPI.SelectionSet {
15 ...
16
17 public var id: String { __data["id"] }
18 public var name: String { __data["name"] }
19 public var friends: [Friend]? { __data["friends"] }
20
21 public var asDroid: AsDroid? { _asInlineFragment() }
22
23 public init(
24 __typename: String,
25 id: String,
26 name: String,
27 friends: [Friend]? = nil
28 ) { ... }
29
30 /// Hero.Friend
31 ///
32 /// Parent Type: `Character`
33 public struct Friend: GraphAPI.SelectionSet {
34 ...
35
36 public var id: String { __data["id"] }
37 public var name: String { __data["name"] }
38
39 public init(
40 __typename: String,
41 id: String,
42 name: String
43 ) { ... }
44 }
45
46 /// Hero.AsDroid
47 ///
48 /// Parent Type: `Droid`
49 public struct AsDroid: GraphAPI.InlineFragment {
50 ...
51
52 public var primaryFunction: String? { __data["primaryFunction"] }
53 public var id: String { __data["id"] }
54 public var name: String { __data["name"] }
55 public var friends: [Friend]? { __data["friends"] }
56
57 public init(
58 primaryFunction: String? = nil,
59 id: String,
60 name: String,
61 friends: [Friend]? = nil
62 ) { ... }
63 }
64 }
65 }
66}
To create an instance of the HerosQuery
class you would do the following:
1let modelData = HerosQuery.Data(
2 hero: HerosQuery.Data.Hero(
3 __typename: "Human",
4 id: "luke-skywalker",
5 name: "Luke Skywalker",
6 friends: [
7 HerosQuery.Data.Hero.Friend(
8 __typename: "Wookie",
9 id: "chewbacca",
10 name: "Chewbacca"
11 )
12 ]
13 )
14)
Type conditions
If you use a type condition in your operation you will notice that a struct is generated to match the type (AsDroid
in the example above) but the generated initializer does not accept a value of that type.
In this case you must create an object of the required type and then use the asRootEntityType
property to initialize the selection set:
1let droid = HerosQuery.Data.Hero.AsDroid(
2 primaryFunction: "Etiquette and translation",
3 id: "c-3po",
4 name: "C-3PO",
5 friends: [
6 HerosQuery.Data.Hero.Friend(
7 __typename: "Droid",
8 id: "r2-d2",
9 name: "R2-D2"
10 )
11 ]
12)
13
14let modelData = HerosQuery.Data(
15 hero: droid.asRootEntityType
16)