GraphQL on SwiftUI: Interfaces, Unions, and Fragments
By: David Choi
Tags: Swiftui, Apollo Client, Swift, GraphQL

GraphQL on SwiftUI: Interfaces, Unions, and Fragments

Learn how to query more sophisticated and complex schema in SwiftUI apps.

I’ve written a book on web development. It is an end to end complete book on modern development practices. Here’s a coupon code to save 20% on Amazon 20FULLS.

In most programming languages you have the idea of a contract. A development tool that allows you to create a set of rules, but with no code implementation. In Swift it’s called Protocols but in most other programming languages it’s known as an Interface.

Regardless of what it’s called GraphQL has a similar capability and it’s also called Interfaces. And Interfaces, along with the ability to inherit from them, allows us greater flexibility around how we design and create our GraphQL schema. We can therefore create a more expressive schema with logical hierarchies that make sense for our application domain. However they also create additional complexity in our resolvers and queries.

In this story we’ll explore what Interfaces are, how we can implement them and how Unions and Fragments can also help us in working with Interfaces.

How to Use Interfaces

I’ll introduce a small GraphQL on Node app that we can use as a sample for Interfaces. First let’s take a look at our schema.

Starting from the top we have a single parent Interface called IVehicle. IVehicle has three fields that are shared across all the other Interfaces and Types in the schema. And as you can see Interfaces can be implemented by other Interfaces, as well as Types. So then finally we see three real Types: Car, Truck, and Boat that inherit from the various Interfaces and have a combination of overlapping and distinct fields.

So far not too complicated, but now how do we use these Types in our queries and mutations? If we look towards the bottom of our schema we can see a line starting with union SearchResult. A Union is a type that can be one of several possible types. In this example the SearchResult Union can be either a Car, Truck or Boat. However it cannot be more than one type at the same time.

Now if we look at the Query we can see one query called getAllvehicles that returns an array of SearchResults. This means that our returned result set can be a combination of our three types: Car, Truck, and Boat.

So then let’s take a look at our resolvers definition.

So when you first see a resolvers implementation for Interfaces and Unions things look a little weird. Why do I need definitions for my Interfaces and Unions? Well in the case of Interfaces it’s because interfaces have no implementation. So there’s no actual data there. Therefore, if for example we look at the IVehicle resolver, we see that it uses some member existence checking logic to determine which hard type should be returned. Again, because an Interface has no actual implementation, only a type does.

Next take a look at the SearchResult Union. Why do we need that? Well as stated a Union can be potentially any one of multiple different types. However it cannot be more than one type at a time. Therefore we need to use logic to tell the GraphQL server which real type to return for a given set of circumstances.

Just one more thing to clarify. The logic in these resolver definitions is entirely up to us. There is no rule about what the logic has to be. However since it should make sense and take into account fields for each type we want, the logic that I am showing would make sense in a lot of scenarios.

So let’s try doing a query on this schema using GraphQL Playgrounds. Here’s a possible query for our getAllVehicles query function.

GetAllVehicles query result

A bit different from your standard query on types. Let’s unpack this. At the top we do the standard thing and indicate we want to run a query. Then we provide the name of the query we intend to run, getAllVehicles. Next we have what are called inline Fragments. A Fragment is sort of like a mini-query. It is a snippet of code that indicates a subset of fields to query. So for this example if we look at the first Fragment for Cars, we see the code for Car is inside of an existing query and that makes it an inline Fragment (I’ll show what a non-inline Fragment is in a bit). We can also see the code starts with “…”. This indicates the start of a Fragment. And the keyword on is a prefix that indicates the Fragment’s type, which of course is Car.

So for this example we’ve indicated that we have this query, getAllVehicles, that may return these three different types. And for each type we only want to see the fields that we’ve indicated. If you look at the result set you will see that the result set’s types and fields match those indicated by the query.

Querying From SwiftUI

In this section we’ll try querying our schema using a SwiftUI app. We’ll use Apollo’s Client for iOS to connect to the GraphQL server. I’ll assume you know how to configure the client, as explaining that would be another story in itself. If you’re not familiar you can try my story on configuring the client here, as it’s non-trivial.

So in order to connect to our GraphQL server we need to have our schema.json file, which is a json representation of the server’s GraphQL schema and the specific query we want to run. To start our query will look like this.

So this is basically the same query we had in our GraphQL Playground except it has a query alias name, GetAllVehicles. We need this name in SwiftUI, as the Apollo Client code generator uses it to create a base class (again if you don’t know what this means you should try going through my previous story before continuing). If you compile your project you should get an API.swift file that you can add to your project.

Now we need a few more files before we can get and display our data. First let’s create a file called Entities that will have our view models representing our data. Which looks like this.

These types will just act as containers for our query data. Next we want a file to create a singleton ApolloClient for our entire app to use.

Now we can create our main ContentView.swift file to present our data.

Let’s take a look at this ContentView. If we start near the bottom we can see an ObservableObject called ObservableVehicle being defined. It contains three arrays: cars, trucks, and boats. Each of which are typed appropriately. Then we have a loadData function, which is what queries our GraphQL server using the ApolloClient we had created previously. The key thing to note here is when getting back a mixed collection of types we need to “cast” the record to the appropriate type using the as<name> property. For example for Cars, that would be asCar. And as you can see if the case is successful we place that object into its appropriate array.

Next if we go back to the top of the file we see that we create an instance of our ObservableVehicle object and then use it to display a simple UI of Text Views for each type’s fields. If you run this you will get this.

Simple UI but clearly we’re able to distinguish the three different types and get only the relevant matching fields back.

Now here’s the thing, if we go back to the query in our SwiftUI app, we can see that the id and name fields keep repeating for each type. Since our types are currently quite simple it does not matter, but if we had a typical large data model it could be 20 or 30 fields that we need to repeat. Let’s try something in the GraphQL Playground. Running this query gives us back the same result set.

Using a fragment

As you can see we’ve removed the id and name fields from each type and added a single Fragment that has those types. So again if we had a lot of fields we could define them only once and reuse them with the same Fragment multiple times.

Unfortunately this query does not work in SwiftUI. It may be that it’s coming later to the Apollo Client or it’s just a bug, but Fragments currently are not working. So then what can we do? Let’s update the query in Playgrounds and try it again.

Using an interface

If we run this query instead we will get back the same data and we won’t have to repeat the fields. This works because every Car, Truck, and Boat type is also an IVehicle Interface and therefore has the fields id and name. So just update your query in SwiftUI, leaving the other code alone, and it will work.

Summary

Sweet. We saw how to do more complex queries within SwiftUI using Interfaces and Unions. And we saw the limitations of Fragments as well (at least for now).

If you have questions ask them on my site DzHaven.com. I will answer them there.

If you like video here’s a youtube version of this story.

And the original Apollo Client for iOS how to configure video.

Happy coding.