Exploring GraphQL on iOS

Exploring GraphQL on iOS

While researching mobile backend as a service (mBaaS) offerings for a client project, I came across Graphcool which provides a GraphQL backend for mobile or web apps. I hadn’t worked with GraphQL before, but it looked interesting and wanted to see if we could put it to use in the mobile or web apps we build. To get a better feel for the tech and tools involved, I decided to update the ALAirports sample project that I’ve used in a few blog posts to use Graphcool as a backend for the airport data.

So What’s GraphQL?

GraphQL is a “query language for your API” developed by Facebook back in 2012 for use in its mobile apps, which in 2015 became a published open source specification and framework. Its development was driven by frustration with the state of REST-like endpoints and development of mobile and web apps to consume them. Some of the problems with REST they were trying to address were:

  • Fetching complicated object graphs typically would require multiple round trips to the server. For example in a blogging app, you may first make a request to fetch all the posts, then for each post you may need to make requests to fetch the comments for those posts. Additionally, these requests often include information that the clients don’t need and as such, waste time and bandwidth.
  • Versioning of REST endpoints can break existing clients, require clients to be updated, complicate or duplicate server code, and can also increase the resulting payload side of responses – again wasting time and bandwidth.
  • Lack of strong typing in JSON responses requiring client side parsing and validation
  • Client or feature specific endpoints developed to solve a particular client side issue later become a server side maintenance problem.

The GraphQL community has done a great job of documenting the schema and query language so I won’t go into too much detail here, but for the purposes of updating the ALAirports app I needed to:

  1. Define a schema
  2. Create some queries and mutations to work with my data
  3. Import the airport data to a Graphcool backend using a mutation
  4. Update the ALAirports app to fetch airport data from the backend using a GraphQL query

Defining a Schema

Editing fields with the Graphcool schema editor

The web based schema editing tools at Graphcool made this pretty trivial. I was able to simply duplicate the existing Core Data Airport model using the schema editor, defining data types and constraints along the way. Once this was out of the way I used the Graphcool data editor to add a few airports by hand. Overall this process was really well documented by Graphcool and was unbelievably easy.

Queries and Mutations

With the GraphQL backend setup with a schema and some data it was time to figure out how to query the backend and to create a mutation to create some airports without using the Graphcool data editor. For this I took advantage of GraphiQL.app which is an Electron based wrapper around the GraphiQL IDE.

Using GraphiQL.app to run a GraphQL query

Using GraphiQL.app to run a GraphQL query

Once you provide GraphiQL your GraphQL API endpoint, it’s able to fetch the related schema and offers code completion which makes defining queries incredibly quick and easy. Using this I was able to create a query to fetch all the airport data along with a mutation that allowed be to create airports using the GraphQL endpoint instead of the Graphcool data editor.

Importing Airport Data

To import a few dozen airports I needed a GraphQL client that could perform the mutation I’d written to create airports using the airport data I had in CSV and JSON files. Given that I wanted to update the ALAirports iOS app to fetch airport data from the backend I used the recommended iOS GraphQL client, Apollo. The Apollo iOS framework is written in Swift and along with apollo-codegen takes you pretty quickly from your schema to making queries.

The process basically looks like:

  1. Install node and npm, then install apollo-codegen using npm. It would be nice if this was a native app and didn’t require node, but given Apollo’s affiliation with Meteor, I can see why this is the case.
  2. Use apollo-codegen to create a JSON version of the schema given the GraphQL endpoint.
  3. Create an iOS project in Swift using Xcode. Install the Apollo framework using one of the three documented methods.
  4. Copy and paste a Run Script build phase from the Apollo docs so that apollo-codegen is run before any Swift code is compiled.
  5. Move the JSON schema file created in step 2 into the project source directory.
  6. Add .graphql files to the Xcode project containing any queries or mutations.
  7. Build the Xcode project and then add the API.swift file generated by apollo-codegen to the project. The apollo-codegen tool generates the API.swift file based on the contents of any .graphql files you’ve added to your Xcode project.
  8. Use the Apollo API and generated code in API.swift to execute GraphQL queries and mutations.

With these steps out of the way, it was simply a matter of loading airport data from a JSON file and calling the method apollo-codegen created in API.swift based on the related .graphql file that I’d created to add airports to my backend. After running the app I was able to confirm that the airports had been added to the backend using the data browser in my Graphcool console and from within the GraphiQL app.

Updating our Sample App

The existing ALAirports app loads airport data from a CSV file into a Core Data store. To update the app to fetch data from my Graphcool backend instead I only needed to:

  1. Update my Airport model object so that it was an NSObject subclass instead of an NSManagedObject. We could have continued to use Core Data here, but in the interest of keeping things simple I decided not to.
  2. Instead of loading data from the Core Data store or CSV file we’d query the Graphcool backend using the code generated by the Apollo framework.

The first step was pretty trivial and the second step was only complicated by the fact that Apollo generates Swift types based on your schema which don’t map easily to Objective-C. I ended up simply returning an array of dictionaries created from the generated Swift structs. It’s awkward, but does the job for now. The complete set of changes I made to the ALAirports app to rely on Apollo, Graphcool, and GraphQL instead of Core Data can be seen in the GraphQL branch of the ALAirports repo on GitHub.

There’s some discussion in the apollo-ios project about introducing support for Objective-C code generation, but it doesn’t feel like a priority for the core team and if it develops, will likely be a community contribution. Given that, if you’re looking to use Apollo I’d recommend developing your app in Swift, or setting aside some time to come up with a reliable way of bridging the data between languages.

Wrapping Up

Overall I found the GraphQL schema and query language to be easy to learn and adopt for a simple project like this, and am curious to learn how that translates to a real world project with a more complex data model. The tools and backend provided by Graphcool were great. Without them I think it would have been far more time consuming to setup a GraphQL backend. The Apollo framework and code generation tools worked well but am curious to learn more about how the code generation and schema to code mapping works, and how customizable that is, if at all.

Based on my limited experience so far, relying on a GraphQL backend looks to be a great way to prototype iOS apps given that Graphcool makes setting up a backend trivial and that Apollo generates the GraphQL client for you. It’s honestly easier than making requests to a REST endpoint and dealing with following on requests or parsing JSON into native objects. However, it’s unclear how you’d provide offline support in a production app, which is a common requirement. There’s some discussion within the Apollo project, and while the client framework offers some caching, there’s not a clear way to support creating content offline and then submit that to the backend once online. Similarly, there’s not a direct approach for handling synchronization or merge conflicts when multiple devices are querying and changing the same data simultaneously. I assume you’d need to develop a custom solution either on the server or within the client app. Despite those concerns, I think GraphQL shows some distinct advantages compared to relying on traditional REST-like endpoints and look forward to learning more about it on future projects.

Header image via graphql.org.

Steven Huey

Steven Huey

Senior Software Engineer at Art & Logic
Mac and iOS developer and project manager at Art & Logic. Twitter | GitHub
Steven Huey

@stevehuey

Father of 4 |  Developer
@InfuseApp Great! Thanks! - 1 month ago
Steven Huey

Tags:

Creative Commons License

This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.