Relay.swift is designed to provide a type-safe way to interact with your app's data. To do this, we use the Relay compiler from the official JavaScript Relay framework with a custom plugin to generate Swift code instead of JavaScript.
The types that are generated are based on both your GraphQL schema and any graphql()
strings found in your Swift source files. Each query
, mutation
, and fragment
in your sources will become a generated .graphql.swift
file.
Let's look at examples of what Relay.swift will generate in different situations.
private let todoFragment = graphql("""
fragment ToDoItem_todo on Todo {
id
text
complete
}
""")
We'll use the above GraphQL fragment as our example. When the Relay compiler runs, it will find this fragment and generate a file named ToDoItem_todo.graphql.swift
.
struct ToDoItem_todo {
var fragmentPointer: FragmentPointer
init(key: ToDoItem_todo_Key) { /* ... */ }
static var node: ReaderFragment { /* ... */ }
}
First, the compiler will generate a struct with the same name as the fragment. You won't generally have to use its properties and methods yourself; they're there to support Relay itself.
This is the type that you will pass to @Fragment in your SwiftUI views.
struct ToDoItem: View {
@Fragment(ToDoItem_todo.self) var todo
// ...
}
extension ToDoItem_todo {
struct Data: Decodable {
var id: String
var text: String
var complete: Bool
}
}
All fragments must have a corresponding Data
type, so the compiler generates a structure that matches the shape of your query. If there are nested fields in the fragment, then it will generate nested types under Data
to represent that data.
The Data
type (and any nested types) conforms to Swift's Decodable
protocol. Relay includes a custom Decoder
implementation specifically for reading your types from the Relay store.
protocol ToDoItem_todo_Key {
var fragment_ToDoItem_todo: FragmentPointer { get }
}
Fragments also get a Key protocol, which is conformed to by any generated types where the fragment is spread. The Key protocol requires that the type includes a field to get a pointer to this fragment's data. The Key is used to pass data between different views while only exposing the right data to each one. Fragment pointers don't actually include the fragment's data. Instead, they have just enough information for Relay to be able to load it in the view that needs it.
private let query = graphql("""
query UserToDoListQuery($id: ID!) {
user(id: $id) {
id
...ToDoList_user
}
}
""")