Alamofire 5 Tutorial for iOS: Getting Started
In this Alamofire tutorial, you’ll build an iOS companion app to perform networking tasks, send request parameters, decode/encode responses and more. By Corey Davis.
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
Alamofire 5 Tutorial for iOS: Getting Started
30 mins
- Getting Started
- Using the SW API
- Understanding HTTP, REST and JSON
- Why Use Alamofire?
- Requesting Data
- Using a Codable Data Model
- Method Chaining
- Setting up Your Table View
- Updating the Detail View Controller
- Fetching Multiple Asynchronous Endpoints
- Creating a Data Model for Starships
- Fetching the Starship Data
- Updating Your Table View
- Sending Parameters With a Request
- Decoding Starships
- Searching for Ships
- Display a Ship’s List of Films
- Where to Go From Here?
Using a Codable Data Model
But, how do you work with the JSON data returned? Working with JSON directly can be messy due to its nested structure, so to help with that, you’ll create models to store your data.
In the Project navigator, find the Networking group and create a new Swift file in that group named Film.swift.
Then, add the following code to it:
struct Film: Decodable {
let id: Int
let title: String
let openingCrawl: String
let director: String
let producer: String
let releaseDate: String
let starships: [String]
enum CodingKeys: String, CodingKey {
case id = "episode_id"
case title
case openingCrawl = "opening_crawl"
case director
case producer
case releaseDate = "release_date"
case starships
}
}
With this code, you’ve created the data properties and coding keys you need to pull data from the API’s film endpoint. Note how the struct is Decodable
, which makes it possible to turn JSON into the data model.
The project defines a protocol — Displayable
— to simplify showing detailed information later in the tutorial. You must make Film
conform to it. Add the following at the end of Film.swift:
extension Film: Displayable {
var titleLabelText: String {
title
}
var subtitleLabelText: String {
"Episode \(String(id))"
}
var item1: (label: String, value: String) {
("DIRECTOR", director)
}
var item2: (label: String, value: String) {
("PRODUCER", producer)
}
var item3: (label: String, value: String) {
("RELEASE DATE", releaseDate)
}
var listTitle: String {
"STARSHIPS"
}
var listItems: [String] {
starships
}
}
This extension allows the detailed information display’s view controller to get the correct labels and values for a film from the model itself.
In the Networking group, create a new Swift file named Films.swift.
Add the following code to the file:
struct Films: Decodable {
let count: Int
let all: [Film]
enum CodingKeys: String, CodingKey {
case count
case all = "results"
}
}
This struct denotes a collection of films. As you previously saw in the console, the endpoint swapi.dev/api/films returns four main values: count
, next
, previous
and results
. For your app, you only need count
and results
, which is why your struct doesn’t have all properties.
The coding keys transform results
from the server into all
. This is because Films.results
doesn’t read as nicely as Films.all
. Again, by conforming the data model to Decodable
, Alamofire will be able to convert the JSON data into your data model.
Codable
, see our tutorial on Encoding and Decoding in Swift.Back in MainTableViewController.swift, in fetchFilms()
, replace:
request.responseJSON { (data) in
print(data)
}
With the following:
request.responseDecodable(of: Films.self) { (response) in
guard let films = response.value else { return }
print(films.all[0].title)
}
Now, rather than converting the response into JSON, you’ll convert it into your internal data model, Films
. For debugging purposes, you print the title of the first film retrieved.
Build and run. In the Xcode console, you’ll see the name of the first film in the array. Your next task is to display the full list of movies.
Method Chaining
Alamofire uses method chaining, which works by connecting the response of one method as the input of another. This not only keeps the code compact, but it also makes your code clearer.
Give it a try now by replacing all of the code in fetchFilms()
with:
AF.request("https://swapi.dev/api/films")
.validate()
.responseDecodable(of: Films.self) { (response) in
guard let films = response.value else { return }
print(films.all[0].title)
}
This single line not only does exactly what took multiple lines to do before, but you also added validation.
From top to bottom, you request the endpoint, validate the response by ensuring the response returned an HTTP status code in the range 200–299 and decode the response into your data model. Nice! :]
Setting up Your Table View
Now, at the top of MainTableViewController
, add the following:
var items: [Displayable] = []
You’ll use this property to store the array of information you get back from the server. For now, it’s an array of films but there’s more coolness coming soon! In fetchFilms()
, replace:
print(films.all[0].title)
With:
self.items = films.all
self.tableView.reloadData()
This assigns all retrieved films to items
and reloads the table view.
To get the table view to show the content, you must make some further changes. Replace the code in tableView(_:numberOfRowsInSection:)
with:
return items.count
This ensures that you show as many cells as there are films.
Next, in tableView(_:cellForRowAt:)
right below the declaration of cell
, add the following lines:
let item = items[indexPath.row]
cell.textLabel?.text = item.titleLabelText
cell.detailTextLabel?.text = item.subtitleLabelText
Here, you set up the cell with the film name and episode ID, using the properties provided via Displayable
.
Build and run. You’ll see a list of films:
Now you’re getting somewhere! You’re pulling data from a server, decoding it into an internal data model, assigning that model to a property in the view controller and using that property to populate a table view.
But, as wonderful as that is, there’s a small problem: When you tap one of the cells, you go to a detail view controller which isn’t updating properly. You’ll fix that next.
Updating the Detail View Controller
First, you’ll register the selected item. Under var items: [Displayable] = []
, add:
var selectedItem: Displayable?
You’ll store the currently-selected film to this property.
Now, replace the code in tableView(_:willSelectRowAt:)
with:
selectedItem = items[indexPath.row]
return indexPath
Here, you’re taking the film from the selected row and saving it to selectedItem
.
Now, in prepare(for:sender:)
, replace:
destinationVC.data = nil
With:
destinationVC.data = selectedItem
This sets the user’s selection as the data to display.
Build and run. Tap any of the films. You should see a detail view that is mostly complete.
Fetching Multiple Asynchronous Endpoints
Up to this point, you’ve only requested films endpoint data, which returns an array of film data in a single request.
If you look at Film
, you’ll see starships
, which is of type [String]
. This property does not contain all of the starship data, but rather an array of endpoints to the starship data. This is a common pattern programmers use to provide access to data without providing more data than necessary.
For example, imagine that you never tap “The Phantom Menace” because, you know, Jar Jar. It’s a waste of resources and bandwidth for the server to send all of the starship data for “The Phantom Menace” because you may not use it. Instead, the server sends you a list of endpoints for each starship so that if you want the starship data, you can fetch it.