Version: 1.1.0

Fetching data with queries

Now that we've set up an environment, we can start fetching data and rendering it in our views. We'll start with something simple: just rendering the ID of the current user. The first thing we want to do is write our GraphQL query into our view's source file, UserView.swift:

import SwiftUI
import RelaySwiftUI
private let query = graphql("""
query UserViewQuery {
viewer {
id
}
}
""")

The graphql function doesn't do anything in our Swift code, but it's a marker that allows the Relay compiler to find our queries and generate code and types for them. Let's generate those now:

$ npx relay-compiler

The compiler should create a file at __generated__/UserViewQuery.graphql.swift. We must add the new file to our Xcode project so it gets built into our app. This file contains types that help Relay execute the query and that allow our view to read the data it returns.

Now we can use our query in a SwiftUI view:

import SwiftUI
import RelaySwiftUI
private let query = graphql("""
query UserViewQuery {
viewer {
id
}
}
""")
struct UserView: View {
@Query<UserViewQuery> var query
var body: some View {
switch query.get() {
case .loading:
Text("Loading...")
case .failure(let error):
Text("Error: \(error.localizedDescription)")
case .success(let data):
Text("User ID: \(data?.viewer?.id ?? "none")")
}
}
}

We're using the @Query property wrapper provided by Relay.swift to load this data in our view. When we try to render the first time, the query will start loading its data and will return .loading from query.get(). Once the request has finished, our view will re-render its body with either .failure or .success as the result.

The data in the .success result has the type UserViewQuery.Data?. This structure is generated by the Relay compiler using our GraphQL schema to match the exact shape of the data requested by our query. We get automatic type-safe access to our data without having to write any serialization code.

This is great: we're able to declare what data our view needs as well as what the view should look like in each possible state for fetching that data.

Dynamic queries using variables#

Suppose we wanted to show information about other users, not just the current one. We can write a query that asks for the node with a given ID, and have our view take that ID in its initializer:

import SwiftUI
import RelaySwiftUI
private let query = graphql("""
query UserViewQuery($userID: ID!) {
node(id: $userID) {
id
}
}
""")
struct UserView: View {
@Query<UserViewQuery> var query
let id: String
var body: some View {
switch query.get(userID: id) {
case .loading:
Text("Loading...")
case .failure(let error):
Text("Error: \(error.localizedDescription)")
case .success(let data):
Text("User ID: \(data?.viewer?.id ?? "none")")
}
}
}

That's a pretty small change from what we had before. Now our GraphQL query takes in a variable called userID. Because we changed the query, we need to run npx relay-compiler to regenerate the corresponding Swift types.

The Relay compiler generated a new variant of query.get for us that matches the variables we declared in our GraphQL query. When we render our view, we pass in the variables we want the query to use. In this case, we get the value for the userID variable as input to our view when it's initialized, but you can also use @State properties or other ways that SwiftUI has to keep track of data.

You can learn more about using queries in the @Query API docs. For now, let's see how to compose views together while loading data with Relay.