GraphQL Using the Apollo Framework: Getting Started
In this Apollo Framework for iOS tutorial, you will learn how to consume GraphQL APIs in a simple and type-safe way. By Felipe Laso-Marsetti.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
GraphQL Using the Apollo Framework: Getting Started
25 mins
- Getting Started
- Running Your GraphQL Server
- Installing the Apollo Framework
- Adding Apollo as a Swift Package
- Adding a Code Generator Script
- Populating the Films Screen
- Writing Your Query Definition
- Implementing Your Network Client
- Retrieving Films Data
- Displaying a List of Films
- Populating the Film Details Screen
- Accessing Film Details Data
- Displaying Film Details
- Displaying the Characters List
- Populating the Character Details Screen
- Accessing Character Details Data
- Displaying Character Details
- Where to Go From Here?
Do you ever feel frustrated when working with a REST API? Do endpoints sometimes not give you the data you need for your app? Do you have to make multiple requests, or change your back end, to get the right information from the server? Well, worry no more! It’s GraphQL and the Apollo Framework to the rescue!
GraphQL is an open-source API design paradigm. Facebook introduced GraphQL in 2012, and it’s been powering Facebook’s mobile apps ever since. In contrast to REST, a GraphQL API exposes only a single endpoint, and the consumers of the API can specify the data they need via a query language.
GraphQL has been open-source since 2015. As its popularity has increased, a robust open-source community has grown around it. This community has developed client libraries and IDE tools. One of the most popular of these projects is Apollo. Apollo is a type-safe caching implementation of GraphQL, available on multiple platforms.
Using the Apollo Framework, you’ll find it simple to:
- Consume a GraphQL schema.
- Auto-generate code from the model specification.
- Fetch data for any GraphQL endpoint.
As you work through the exercises in this tutorial, you’ll focus on setting up a local GraphQL development environment and fetching data from it. In the process, you’ll learn how to use the Apollo Framework to generate Swift code based on server queries and data schema. And you’ll use it to communicate with your server to fetch the data you need.
Getting Started
In this tutorial, you’ll build an app to display information about the films in the Star Wars universe. Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Go to the Starter folder and open JediArchives.xcodeproj inside the Jedi Archives folder.
Now take a look at the project structure.
This project has three view controllers:
- FilmsViewController contains the list of movies.
- FilmDetailsViewController contains the movie information, such as the director and release date.
- CharacterDetailsViewController contains information about the characters in the selected movie.
The project also includes some interesting files needed for Apollo and GraphQL. You’ll see how they work as you go through the exercises in the tutorial:
- Apollo.swift: You’ll create a client to interface with the Apollo framework here.
- Queries.graphql: Your GraphQL queries will go here.
- API.swift: The Apollo Framework will automatically generate the model and API Swift code here.
Now, build and run. You’ll see the application — but you won’t see any films in the list!
The app’s user interface is complete, but empty. You’ll use Apollo to read the information from a server and display all the awesome Star Wars movies in the app. To do that, you’ll need a GraphQL server to provide the data. You’ll set up that server next.
Running Your GraphQL Server
You’re going to use the Star Wars API to get the movie information for the application. This API uses REST services. And, unfortunately, that means you can’t access it using GraphQL.
Fortunately, though, there’s an open-source project for this! The SWAPI GraphQL wrapper uses GraphQL to access the Star Wars API.
In the tutorial materials, you’ll find a SWAPI GraphQL folder which contains a pre-configured Node.js project that creates a local server. It then uses the SWAPI GraphQL wrapper to serve the information to your app using GraphQL.
To run the server, you need to make sure you have Node.js installed. If you don’t have it, visit Node.js for installation instructions.
With Node.js ready, it’s time to install Yarn. You’ll use Yarn to build and run the server. Open a terminal window and run the following command:
sudo npm install -g yarn
Next, you’ll build and run your GraphQL server. This is the server your app will connect to.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
Then, to complete your Homebrew installation, run the following command to install pkg-config:
brew install pkg-config
Now go to the tutorial materials. You’ll find the server in the SWAPI GraphQL folder. Navigate to that folder. In your terminal window, type the following command:
cd
Be sure to add a space following the cd
command. Then, drag the SWAPI GraphQL folder and drop it into the terminal. This will write the folder location in the terminal, and you won’t need to type it. Press Enter to change to the new directory.
Now, to build the server, run this command:
yarn
Wait for the command to finish, and then run the following command to start your GraphQL server:
yarn start
After you run this last command, your terminal window will display a localhost URL as well as a port, like this:
$ node lib/server
Listening at http://localhost:57881
Make a note of the port number. You’ll need it later for your app to communicate with the local server.
Great work! Now you’re all set up on the server side, and you’re ready to work on your app.
Installing the Apollo Framework
With your server up and running, it’s time to install the Apollo Framework. You’ll use the Swift Package Manager for this.
Adding Apollo as a Swift Package
Go back to Xcode, and select File ▸ Swift Packages ▸ Add Package Dependency….
In the next screen, paste https://github.com/apollographql/apollo-ios.git into the Choose Package Repository search box. Click Next. This will add Apollo as a Swift package.
Now, select Up To Next Minor and click Next.
In the next screen, select the Apollo package and click Finish.
Xcode will download the necessary packages and add the dependencies to your project.
Awesome! You’ve added Apollo as a Swift package, but there are still a few more steps needed to get it all working.
Adding a Code Generator Script
Apollo requires a GraphQL schema file to generate the code. This schema is a JSON file that contains information from an introspection query. The schema describes the types and fields you can query. It also includes any relationships between those types. The file is usually called schema.json. You can find it inside the Jedi Archives folder of your project.
Now you need to add the script that will generate the code for your queries. Select your project in the Project navigator. Then select the Jedi Archives target, and click Build Phases:
Click the + button and select New Run Script Phase.
Paste the following script into the window to let Apollo work its code-gen magic:
# Go to the build root and search up the chain to find the
# Derived Data Path where the source packages are checked out.
DERIVED_DATA_CANDIDATE="${BUILD_ROOT}"
while ! [ -d "${DERIVED_DATA_CANDIDATE}/SourcePackages" ]; do
if [ "${DERIVED_DATA_CANDIDATE}" = / ]; then
echo >&2 "error: Unable to locate SourcePackages directory from BUILD_ROOT: '${BUILD_ROOT}'"
exit 1
fi
DERIVED_DATA_CANDIDATE="$(dirname "${DERIVED_DATA_CANDIDATE}")"
done
# Grab a reference to the directory where scripts are checked out
SCRIPT_PATH="${DERIVED_DATA_CANDIDATE}/SourcePackages/checkouts/apollo-ios/scripts"
if [ -z "${SCRIPT_PATH}" ]; then
echo >&2 "error: Couldn't find the CLI script in your checked out SPM packages; make sure to add the framework to your project."
exit 1
fi
cd "${SRCROOT}/${TARGET_NAME}"
"${SCRIPT_PATH}"/run-bundled-codegen.sh codegen:generate --target=swift --includes=./**/*.graphql --localSchemaFile="schema.json" GraphQL/API.swift
This script sets up all the environment variables needed by Apollo’s code generator. It then launches the code generator, using schema.json as a reference to generate the code. The generated code will be saved in API.swift.
Now, click the title of your new build phase, and rename it to Generate Apollo GraphQL API. Then, drag and drop it below the Dependencies build phase. This is what your build phases will look like after that:
If you try to build your project right now, you’ll get an error. This is because you haven’t written any GraphQL queries, and the script will fail. You’ll take care of that in the next step!
Populating the Films Screen
Now it’s time to write your GraphQL queries. But how do you know what to write? How do you even know what a GraphQL query can do? Fortunately, the SWAPI GraphQL wrapper has a cool GraphQL playground. You can use this playground to experiment all you want with GraphQL queries!
Writing Your Query Definition
You’re going to write a query to retrieve data for all the Star Wars films. You need the film names and information associated with each film. And you need the characters in those films.
Open Queries.graphql and paste the following queries:
# 1
query AllFilms {
# 2
allFilms {
# 3
films {
# 4
id
director
episodeID
title
releaseDate
#5
characterConnection(first: 10) {
# 6
characters {
# 7
id
name
birthYear
eyeColor
hairColor
# 8
homeworld {
# 9
name
}
}
}
}
}
}
Here’s what you’re doing in this code:
- You define a query called
AllFilms
. The query will fetch the desired films along with information about them. - You specify that you want all the films available.
- For each film within your collection, what attributes and values do you want to get? This is how you define the scope of data to fetch for an individual film.
- You want the ID, director, episode ID, title, and release date for each film.
- For an individual film, you also want to fetch the first ten characters associated with it.
- For each character, you specify what information to retrieve.
- You want the ID, name, birth year, eye color, and hair color of each character.
- You also want the homeworld for the character.
- And, for that homeworld, you need its name.
As you can see, it looks a bit Swift-y or JSON-y. Feel free to experiment with the GraphQL playground for SWAPI. Add or remove things in the query, run it, and check out the results.
Build and run. Now API.swift contains some generated models and the Swift API!
Open API.swift, but don’t modify it. This is the generated code and models your app will use. Feel free to look at the code and see what Apollo generated for you. The most important are these:
-
AllFilmsQuery
: This is the query class you’ll use to access the server data. -
Film
: This structure contains the data for every movie. It includes the fields you specified in the query file generated by Apollo. -
Character
: This structure contains the character information. It has the properties you defined in the query.
You’ll use the data structures in the API to display the movie information in the app. But first, you need to implement the network client so that you can retrieve your data.
Implementing Your Network Client
Open Apollo.swift and add an import for the Apollo framework at the top of the file:
import Apollo
Then, add the following code inside Apollo
:
// 1
static let shared = Apollo()
// 2
let client: ApolloClient
// 3
init() {
client = ApolloClient(url: URL(string: "http://localhost:49241")!)
}
This is what your code does:
- You define Apollo as a singleton, since you only want one instance of the Apollo client for the app.
- You define a property to store the actual
ApolloClient
provided by the Apollo Framework. - You instantiate a new
ApolloClient
with the location of your GraphQL server. You’ll need to replace 49241 with your local server port. This is the port your local GraphQL server returned after you ran the yarn start command, above.
Now you’re ready to start working with Star Wars data!
Retrieving Films Data
Switch to FilmsViewController.swift. Replace the whole line where you declare the films
property with the following:
var films: [AllFilmsQuery.Data.AllFilm.Film] = []
Here you change films
from an array of strings to an array of Film
objects. Apollo automatically generated that structure from the information in the queries file. Notice how Film
resides inside AllFilmsQuery
, within its Data
and AllFilm
structures.
Moving on, add the following code inside loadData()
:
// 1
let query = AllFilmsQuery()
// 2
Apollo.shared.client.fetch(query: query) { result in
// 3
switch result {
case .success(let graphQLResult):
if let films = graphQLResult.data?.allFilms?.films?.compactMap({ $0 }) {
// 4
self.films = films
self.tableView.reloadData()
}
case .failure(let error):
// 5
print("Error loading data \(error)")
}
}
In this code, you do the following:
- First, you create an
AllFilmsQuery
object. - Then you use the Apollo client you created earlier to fetch the data. You use the
AllFilmsQuery
query to do this. - In the closure, you check whether the query succeeded or failed.
- If the query succeeded, you retrieve the result list into
films
. - If the query failed, you print an error to the console so you can find out what went wrong.
Next, you’ll display your data in the main table view.
Displaying a List of Films
To display your films list, you’ll start by going to tableView(_:cellForRowAt:)
. Add the following code just before the return
statement:
let film = films[indexPath.row]
cell.textLabel?.text = film.title
Here you fetch the Film
for the given row. You set the label text for the cell to the film’s title. This is all pretty standard table view and cell work. :]
Build and run, and admire the list of movies you created:
Sweet! You’re now fetching data, via GraphQL, and displaying it within a table view in your app. Great work!
Populating the Film Details Screen
At this point, if you tap one of the film cells, the app will crash. That’s OK! You’re going to focus on this next.
Accessing Film Details Data
Again, go to FilmsViewController.swift. Replace the code in showFilmDetails(_:sender:)
with the following:
guard
let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell)
else {
return nil
}
return FilmDetailsViewController(film: films[indexPath.row], coder: coder)
This code retrieves the index of the cell that was tapped. It returns a new FilmDetailsViewController
for the specified Film
. If a problem occurs and it can’t get the desired value, then it returns nil
.
If you try to build now, you’ll get an error on the last sentence. This is because you previously changed the type of the film, which is now a type generated by Apollo. To fix it, go to FilmDetailsViewController.swift. At the beginning of the class, replace the film
declaration with the following:
private let film: AllFilmsQuery.Data.AllFilm.Film
This is the same concept as when you created a property for your films
array. This one, though, is for a single film.
Now you need to change the class initializer to accept this new type. Delete init?(film:coder)
and replace it with this code:
init?(film: AllFilmsQuery.Data.AllFilm.Film, coder: NSCoder) {
self.film = film
super.init(coder: coder)
}
This new initializer receives a Film
instead of a String
.
Displaying Film Details
Now go to viewDidLoad()
and add the following code:
title = film.title
This displays the film’s title in the navigation bar.
Next, you need to display the right number of rows, depending on the number of characters this film has. Scroll down to tableView(_:numberOfRowsInSection:)
. Replace the line that reads return 0
with the following:
return film.characterConnection?.characters?.count ?? 0
Here you return the number of characters associated with the current film. Notice how you access the characters count. The count comes from the characters
array in the Film
‘s characterConnection
. Since characters
is optional, you must allow for the case where there are no characters. If this happens, you return zero.
Now you’re ready for the last step. You need to display the movie information in the table’s cells. Go to tableView(_:cellForRowAt:)
and locate this block of code:
if indexPath.row == 0 {
cell.textLabel?.text = "Episode"
} else if indexPath.row == 1 {
cell.textLabel?.text = "Released"
} else if indexPath.row == 2 {
cell.textLabel?.text = "Director"
}
Replace it with this:
if indexPath.row == 0 {
cell.textLabel?.text = "Episode"
if let episodeNumber = film.episodeId {
cell.detailTextLabel?.text = "\(episodeNumber)"
}
} else if indexPath.row == 1 {
cell.textLabel?.text = "Released"
cell.detailTextLabel?.text = film.releaseDate
} else if indexPath.row == 2 {
cell.textLabel?.text = "Director"
cell.detailTextLabel?.text = film.director
}
This code retrieves the film’s episode number, release date and director. It then assigns the information to the text fields of the appropriate cells based on the row number.
Now build and run, and tap one of the films.
Great! You’re displaying the film details!
But, if you look closely, the characters list is just a bunch of repeated cells. The good news is that you’re showing the correct number of cells for the characters in this film. But now you need to update the code to display the characters’ names.
Displaying the Characters List
Again, go to tableView(_:cellForRowAt:)
Add the following code just before the last return
statement of the method:
cell.textLabel?.text = film.characterConnection?.characters?[indexPath.row]?.name
This sets the name of the character in the cell’s text field.
Build and run one more time, and check out the results:
Yay! The characters list is looking much, much better. :]
Populating the Character Details Screen
Earlier you found that tapping a film cell caused the app to crash. Now, tapping a character cell will now crash your app! And as you did for the film details, you’ll correct this by passing along the character to the character details screen.
Accessing Character Details Data
Go back to FilmDetailsViewController.swift. Replace the code in showCharacterDetails(_:sender:)
with the following:
guard
let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell),
let character = film.characterConnection?.characters?[indexPath.row]
else {
return nil
}
return CharacterDetailsViewController(character: character, coder: coder)
Here you retrieve the index of the selected row. You use it to initialize CharacterDetailsViewController
with the selected character.
If you try to build now, you’ll see an error because the character’s type has changed. To fix it, open CharacterDetailsViewController.swift. Inside the class implementation, replace the line declaring character
with the following:
let character: AllFilmsQuery.Data.AllFilm.Film.CharacterConnection.Character
This will look very familiar to you. It’s similar to what you did for an array of films, and for a single film. Here you create a property of type Character
. You’ll use this property to populate the character details screen.
And, as you saw before, an error occurs. This is because the initializer expects a different type for the character.
Delete init?(character:coder:)
and replace it with the following:
init?(
character: AllFilmsQuery.Data.AllFilm.Film.CharacterConnection.Character,
coder: NSCoder
) {
self.character = character
super.init(coder: coder)
}
This initializer uses the new Character
type and sets the value of character
.
Displaying Character Details
Now, go to viewDidLoad()
and add the following code at the end:
title = character.name
This displays the name of the character in the navigation bar.
Next, go to tableView(_:cellForRowAt:)
. Replace the existing block of if
and else if
statements with the following:
if indexPath.row == 0 {
cell.textLabel?.text = "Birth Year"
cell.detailTextLabel?.text = character.birthYear
} else if indexPath.row == 1 {
cell.textLabel?.text = "Eye Color"
cell.detailTextLabel?.text = character.eyeColor
} else if indexPath.row == 2 {
cell.textLabel?.text = "Hair Color"
cell.detailTextLabel?.text = character.hairColor
} else if indexPath.row == 3 {
cell.textLabel?.text = "Home Planet"
cell.detailTextLabel?.text = character.homeworld?.name
}
This code inserts the appropriate text into each cell, using the row number to populate the cells correctly.
Your code is now complete! Woohoo!
Build and run. Check out the results by navigating to a film’s detail screen and then selecting a character. You’ll now see the character’s information in the screen.
Awesome work completing the tutorial! Congratulations!
Where to Go From Here?
You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial.
You’ve now seen the power of GraphQL. You’ve written queries that are very easy to understand and interpret. Apollo generated the necessary Swift API code for you. Then you called your queries via code to get the data you need.
If you want to dive further into GraphQL and Apollo, you can read the Introduction to GraphQL and Apollo’s docs. Try expanding Jedi Archives to make additional queries and return different data. Or experiment with other GraphQL servers! You can use their APIs to retrieve completely different data.
I hope you enjoyed this tutorial as much as I did. If you have any questions or comments, please join the discussion below. May the code be with you!
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more