SwiftGraphQL lets you query data, perform mutations and even listen for subscriptions. Even though each method does something different, they all share the principle of query building. This section shows how to create a query using SwiftGraphQL query builder.

If you are interested in more in-depth overview of how selection works, check out selection documentation.

Simple Queries

To kick things off, let’s start with a simple Hello World query:

let hello = Selection.Query<String> { builder in
    try builder.world()
}

One by one we

  1. first create a Selection.Query since we are going to select a field in the root Query. This is a generic type that can be used to query any type of data. In this case, we’re querying a String value, that’s why we particularize the generic to return a string.
  2. Second, we create a closure that will be called to create a selection and to decode returned value. That closure will receive a QueryBuilder as the only argument which lets us select fields available in that type - in our case world field in the Query type.
  3. Lastly, we assign the selection to a value hello so we can reuse it in many places.

Reusing Selection

Once we’ve create a selection, we can reuse it in any other query just like a GraphQL fragment. This way, we can compose complex queries from simple ones and keep track of all the selections we’ve created.

let picture = Selection.Picture<Picture> {
    let id = try $0.id()
    let url = try $0.url()

    return Picture(id: id, url: url)
}

let user = Selection.User<User> {
    let id = try $0.id()
    let name = try $0.name()
    let avatar = try $0.picture(selection: picture.nullable)

    return User(id: id, name: name, avatar: avatar)
}

let message = Selection.Message<Message> {
    let id = try $0.id()
    let createdAt = try $0.createdAt()
    let text = try $0.text()
    let author = try $0.author(selection: user)

    return Message(id: id, createdAt: createdAt, text: text, author: author)
}

let feed = Selection.Query<[Message]> {
    try $0.feed(selection: message.list)
}

Notice that instead of always relying on builder argument, we use $0 to refer to the QueryBuilder instance.

Decoding Values

Since selection is just another Swift function, we can add custom logic to how we decode values. For example,

  • we might want to make sure that each message has an author even if schema doesn’t enforce it,
  • we could convert a String value to an URL and fail if it’s not a valid URL,
  • we could convert an object into a discriminated union, or
  • we could even filter out the data from a list that doesn’t fit our needs.
enum FeedItem {
    case ad(url: URL)
    case message(text: String)
    case post(message: String, photo: URL)
}

let item = Selection.Item<FeedItem> {
    let kind = try $0.kind()

    let url = try $0.url()
    let text = try $0.text()
    let message = try $0.message()
    let photo = try $0.photo()

    switch kind {
    case .ad:
        guard let url = URL(string: url) else {
            throw GraphQLError.invalidURL(url)
        }
        return .ad(url: url)
    case .message:
        guard let message = message else {
            throw GraphQLError.missingMessageContent
        }
        return .message(text: text)
    case .post:
        guard let message = message, let photo = photo else {
            throw GraphQLError.missingPostContent
        }
        return .post(message: message, photo: photo)
    }
}

It’s important that you always make selection before any logical operation on it. Otherwise, it could happen that some fields are missing from the selection.

Adding Arguments

GraphQL operations often require arguments. SwiftGraphQL lets you pass arguments to your selections as arguments to selected fields. Furthermore, if you want to make your query dynamic, you can create a function that returns selection instead of creating a variable.

let date = Selection.Query<Date> {
    try $0.date(format: "yyyy-MM-dd")
}

func login(email: String, password: String) -> Selection.Mutation<String?> {
    Selection.Mutation<String?> {
        try $0.login(email: email, password: password)
    }
}

Optional Arguments

GraphQL differentiates between different kinds of absent variables when dealing with inputs. A variable may be

  • present (e.g. “21 years old”)
  • null (e.g. “we don’t know how old a person is”)
  • missing (e.g. “we shouldn’t change the current value”).

To present all cases, SwiftGraphQL uses OptionalArgument enumerator.

enum OptionalArgument<Value> {
	case present(Value?)
	case absent
}

NOTE: Every nullable argument is by default absent so you don’t have to write boilerplate code.

Because writing OptionalArgument is cumbersome, SwiftGraphQL provides a convenience operator ~ that transforms scalar values into present optional arguments.

let variable: OptionalArgument<Int> = ~21

let query = Selection.Query<String?> {
	let user = $0.user(id: ~"uid-17032000", selection: User.selection)
	return user.name
}

let mutation = Selection.Mutation<Void> {
	try $0.createUser(name: ~"John Doe", age: ~nil)
}