Getting Started With GraphQL and Apollo on iOS
A step by step guide from installation and database creation to your first app with Swift and GraphQL using Apollo.
GraphQL is a powerful tool, in this article you will learn how to use it on iOS starting from scratches using Swift.
Some Background
As Wikipedia says
GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data.
It provides an approach to developing web APIs, and has been compared and contrasted with REST and other web service architectures. It allows clients to define the structure of the data required, and the same structure of the data is returned from the server, therefore preventing excessively large amounts of data from being returned, but this has implications for how effective web caching of query results can be. The flexibility and richness of the query language also adds complexity that may not be worthwhile for simple APIs. It consists of a type system, query language and execution semantics, static validation, and type introspection.
I don’t have the authority to say that GraphQL is better than Restful APIs, but it brings many benefits and flexibility that we cannot find in the latest, for example:
- Instead of calling multiple endpoints to fetch data we have a single endpoint
- It offers a strong type system as we will see later in the article
- We fetch the exact data that we need without any additional useless information or needing to do multiple calls to multiple endpoints to get all the information that we need
Creating a simple PostgreSQL database
Let’s create a simple database with Postgres.
Installation
We can basically install Postgres in 2 ways:
- Downloading Postgres.app
- Manually with Homebrew and Terminal
With Postgres.app
It’s pretty straightforward:
- Download the app from the following link
- Move it do the Application folder
- Open it and click on “initialize”
Done! You now have a PostgreSQL server running on your Mac with these default settings:
Manual Installation
If you opted for the manual solution let’s start by installing Homebrew, we’ll follow this guide.
- Run the following command in your terminal
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
2. Then we can install Postgres
brew install postgresql
3. Run the following command
ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents
4. Let’s create two aliases to start and stop the postgres service
alias pgstart="launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist"
alias pgstop="launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist"
5. Run the alias we just created to see if it works
pgstart
If we don’t get any error it worked correctly.
6.Create a simple database
createdb
7.Connect to postgres running the command
psql
8. If you see the following error
role "postgres" does not exist
You can fix it by running
createuser -s postgres
If everything worked fine you should see this
Connecting Postgres to GraphQL
There are several ways to do this, which require prior knowledge with node.js, I want this guide to be noob friendly so we are going to use Hasura, an engine that generates a complete GraphQL backend from our database.
It can run on Docker or Heroku, since for this tutorial we set up a local database we will use the Docker approach.
Let’s get started, we need to install Docker.
You can download the docker installer from here.
Get the docker-compose file
Let’s create a file named docker-compose.yaml that will contain our database configuration and fill it with our database info
version: '3.6'
services:
postgres:
image: postgres:12
restart: always
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgrespassword
graphql-engine:
image: hasura/graphql-engine:v1.2.1
ports:
- "8080:8080"
depends_on:
- "postgres"
restart: always
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
## uncomment next line to set an admin secret
# HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
volumes:
db_data:
Change the following line
postgres://postgres:postgrespassword@postgres:5432/postgres
To
postgres://yourUsername:@host.docker.internal:5432/postgres
If you don’t know what’s your username just run
psql\du
And insert your role name
place it in our project directory and run in terminal
cd yourProjectDirectory
Run Hasura GraphQL engine & Postgres
$ docker-compose up -d
Check if the containers are running:
$ docker psCONTAINER ID IMAGE ... CREATED STATUS PORTS ...
097f58433a2b hasura/graphql-engine ... 1m ago Up 1m 8080->8080/tcp ...
b0b1aac0508d postgres ... 1m ago Up 1m 5432/tcp ...
And your docker should look like this
If everything worked, you can access the Hasura console
http://localhost:8080/console/
Set up our database
If you did everything right you should see the Hasura console
Click on “Data” and then “Create Table” to create our first database table
We are going to create a really simple “User” table that will allow us to store users, therefore it will contain the following fields:
- id: it will have uuid type and it will be self-generated and unique
- username: character varying type (string), and unique
- password: character varying type, unique
- email: character varying type, nullable, unique
Let’s set id as primary key and our table should look like this
Click on add table to finalize the creation
Apollo iOS Installation
As they state in their documentation
Apollo iOS is a strongly-typed, caching GraphQL client for native iOS apps written in Swift.
It allows you to execute queries and mutations against a GraphQL server and returns results as query-specific Swift types. This means you don’t have to deal with parsing JSON, or passing around dictionaries and making clients cast values to the right type manually. You also don’t have to write model types yourself, because these are generated from the GraphQL definitions your UI uses.
Go in your project directory with terminal and type
pod init
add the Apollo iOS dependency
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'target 'ApolloExample' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!# Pods for ApolloExample
pod "Apollo"end
and run
pod install
Double click on our .xcworkspace file just created, the project should look like this
Downloading The Database Schema
In the same directory run the following command to download the schema of your database
apollo schema:download --endpoint http://0.0.0.0:8080/v1/graphql
You should see a schema.json file in your project directory containing the database schema
Move it in our target directory
Writing our first Mutation
A mutation is basically a query that modifies data in the database and returns a value.
We are going to create a “signUp” mutation to create users; let’s click on “GraphiQL” in the Hasura console, then on the bottom left select “add new mutation” and click on “+”
We will see a new empty mutation that looks like this
Hasura allows us to easily create Queries and Mutations with the Explorer tool.
Click on “insert_User” and then we can select the fields required for the creation; under the “objects” menu select “username”, “password” and “email”.
Under the “returning” menu select “id” to return the generated user-id; we will see the following
But we are missing the mutation parameter, let’s add them
mutation signUp
($username:String!,$password:String!,$email:String) {
insert_User(objects: {username: $username, password: $password, email: $email}) {
returning {
id
}
}
}
Now try to run it and you should see the following error
Now copy the mutation and paste it in a new “mutations.graphql” file in your project target directory, the same where we placed “schema.json” and add it to the project.
Code Generation Step
We need to add a script that uses our schema and .graphql file to generate the API code containing the queries and mutations, and parse responses.
Click on your project target, select “Build phases”, click on the “+” button to create a new step, select “new run script phase”, and drag it before “Compile sources”
Code generation uses your .graphql
files to generate API code that will help you send queries, subscriptions, and mutations, as well as parse and cache responses. To run code generation as part of the Xcode build process, you need to create a build step that runs before "Compile Sources" to invoke a wrapper script.
Paste the following code in the script text box and try to build the project.
If you did everything correctly, a new “API.swift” file will be generated in the target directory; let’s add it to our project by dragging it into Xcode
Let’s Register Our First User
First of all, we need to implement the apollo client for the network calls; let’s create a “Network” class as explained here https://www.apollographql.com/docs/ios/initialization/#basic-client-creation
I’m going to create a simple SwiftUI view containing the text fields and a sign-up button.
It will look like this
Let’s create also the model and add the mutation code
Then we need to edit the ContentView class and put SignUpView() inside the body
Let’s launch the app and try to register a new user; if everything worked you will see the following output
And we can also check our table
Login Query Creation
Let’s create a login query in the same way we did for the mutation
Paste it in out .graphql file
And build again to generate the query into the API.swift file
Let’s now create our LoginView
And LoginViewModel
As you can see we use the “fetch” method to perform queries since we’re not going to modify the data.
Don’t forget to set LoginView in the ContentView body as we did before
Let’s try it
It worked.
You can find the example code here:
https://github.com/ilahomesick/ApolloGraphQLExample
I hoped you enjoyed the article and that you were able to set up everything properly; if you had any problem feel free to contact me and I’ll be happy to help.