GraphQL for iOS Apps
The typical way to write iOS apps
The majority of modern mobile apps are just beautiful representations of JSON — news feeds, live scores, messengers and so on. Usually, an app performs a request to a REST endpoint, receives a JSON response, converts it into a model object and finally displays it on screen.
iOS developers have been comfortable with this approach for several years, so there are now a lot of libraries, APIs and techniques that enable building such apps. But it feels like there should be some room for improvement. Let’s identify the weak points and think about better solutions.
What’s wrong with REST
When iOS developers start working on a network-based feature, they are usually given an existing server endpoint with, hopefully, thorough documentation. It’s always good news if the API is optimized for mobile clients, but quite often it is just a representation of the server model. The problem is that apps usually require a slightly different set of data than the endpoint provides.
Let’s imagine that we are building a GitHub team browser, and we need to show the list of the team members’ names along with the names of their repos, but there is only an endpoint that returns team members with full information along with IDs of the repos. Of course, we can ask the backend team to introduce a new endpoint or a special parameter to obtain just a subset of objects or fields:
GET /members/1213?listPreview=trueGET /membersAndRepos/1213
But it’s not a good solution because:
- the backend team may not be available;
- it takes time to implement, test and document more endpoints;
- the app requirements may change, so these additions will become redundant.
So, what naturally has to happen is downloading unnecessary data in responses and performing excessive requests. What if there was a nice way for the client app to specify what data it needs?
What is GraphQL and why it’s cool
GraphQL is the approach that makes it possible. According to the official site, “GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and is instead backed by your existing code and data.”
GraphQL was born in Facebook in 2012. Their mobile team was working on the new feed when they realized that the existing techniques were far from perfect for mobile apps — back then, the internet connection was slow, so optimizing queries would drastically improve user experience.
After the internal success, the team open sourced GraphQL in 2015. Now GraphQL is an RFC spec, and there are many implementations of it in multiple languages.
Apart from Facebook itself, some big companies have already introduced GraphQL APIs: GitHub and Shopify.
As the name implies, GraphQL is a “Query Language” to a graph. As the data inside most of the modern apps can be represented as a graph, GraphQL is a convenient solution.
The most amazing part about GraphQL is its simple flow:
- Describe your data.
- Ask for what you want.
- Get predictable results.
Describing data
Every GraphQL service has a schema — a blueprint of what data is available on the server. What’s particularly cool about GraphQL schemas is strong types. Also, the schema.json
file acts like a documentation for the server.
Continuing our repos example, we could have a schema like this:
type User {
name: String!
avatarUrl: String
contributedRepositories: [Repository]!
}
Requesting data
Once you know what data the server is able to provide, you can start to send queries. Only one endpoint is required — it should handle all of your requests.
Queries resemble JSON:
{
viewer {
name
contributedRepositories {
name
}
}
}
You specify only those fields that you want to use further in your app. Except for basic features like arguments and variables, queries can also be powered by aliases, fragments, directives and other cool things:
query Member($login: String!) {
user(login: $login) {
...UserDetails
}
}fragment UserDetails on User {
name
avatarUrl
contributedRepositories(first: 100) {
...RepoDetails
}
}
Getting results
As you can see below, the result has exactly the same shape as the query. So, you always get the expected result:
{
"data": {
"viewer": {
"name": "John Smith",
"contributedRepositories": [
{
"name": "tools-for-ninjas"
},
{
"name": "amazing-graphql"
}
]
}
}
}
There are several advantages in comparison with the traditional REST way:
- one request to coalesce several API calls;
- response is a sub-graph of the whole app graph;
- strong types from the server lead to strong types on the client;
- response is JSON-formatted, it’s easy to parse and convert into model.
All those things improve both developer and user experience.
How to make GraphQL work on iOS
iOS doesn’t support GraphQL out of the box. Unfortunately, Facebook hasn’t released any library to work with GraphQL on iOS either. So, we can rely only on third-party implementations or do it ourselves.
Ideally, we would like to make use of strong types (GraphQL schema → Swift model), automatic serialization and some caching.
There are three possible approaches:
- Naive — manually hard-code or compose requests using query-builder libraries, then use NSJSONSerialization, multiple
if let
s and a data layer for caching. - Web-oriented — React Native + Relay — if you are familiar with JS and front-end development.
- Native — Apollo iOS.
Apollo iOS
Apollo iOS is an open-sourced GraphQL client for native iOS apps, written in Swift and wrapped into a pod. It has strong types and caching built-in. And it is nicely integrated with Xcode. How does it work?
First, Apollo generates Swift classes for your GraphQL queries and Swift structs for responses based on the downloaded schema and .graphql
files. All the types are placed into the API.swift
file. This is done automatically with a custom Xcode build phase.
When you need to make a query against a GraphQL server, you just construct a fetch request object, pass it into the ApolloClient and receive a struct. Apollo also supports mutations — they are just as easy as fetches. Serialization is performed automatically, so you don’t have to deal with parsing JSON, passing dictionaries and manual casting.
In addition, Apollo caches the response using the principle “same path = same object”. You can specify your own cache IDs as well. The cache is kept up to date as further queries and mutations are performed.
One more nice feature is watching. It is similar to fetch, but in addition to receiving an initial response, your result handler will be called every time the corresponding data in the cache changes.
Altogether, Apollo gives everything you need to build native iOS apps against a GraphQL-powered backend.
Summary
Now, when we know that there is a good way to write native apps talking to GraphQL APIs, let’s revisit all the pros and cons of the Apollo+GraphQL approach.
In my opinion, the killer features are:
- combining multiple network requests into one;
- specifying only the data you are interested in.
These properties lead to fewer requests and smaller payload which can dramatically improve user experience.
Other advantages are:
- client-side developers are in control of what data is sent;
- static type safety: from server to app UI;
- nice tools (for example, GraphiQL for exploring GraphQL APIs);
- bright community.
Of course, there are some downsides:
- not a silver bullet, you still need REST, for example, to download resources or upload files;
- takes time to learn and adopt;
- no out-of-the-box support on iOS;
- no way to inject custom code (transformers) into the mapping process;
- some glitches in Xcode code-generation.
Overall, GraphQL is a powerful technology, and we at Castle are happy to be able to use it in our mobile apps.