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.

Fragments

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.

The fragment type

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

		// ...
}

The Data struct

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.

The Key protocol

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.

Operations (queries and mutations)

private let query = graphql("""
query UserToDoListQuery($id: ID!) {
    user(id: $id) {
        id
        ...ToDoList_user
    }
}
""")