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.

4.6 (54) · 3 Reviews

Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Creating a Data Model for Starships

Before fetching any starships, you first need a new data model to handle the starship data. Your next step is to create one.

In the Networking group, add a new Swift file. Name it Starship.swift and add the following code:

struct Starship: Decodable {
  var name: String
  var model: String
  var manufacturer: String
  var cost: String
  var length: String
  var maximumSpeed: String
  var crewTotal: String
  var passengerTotal: String
  var cargoCapacity: String
  var consumables: String
  var hyperdriveRating: String
  var starshipClass: String
  var films: [String]
  
  enum CodingKeys: String, CodingKey {
    case name
    case model
    case manufacturer
    case cost = "cost_in_credits"
    case length
    case maximumSpeed = "max_atmosphering_speed"
    case crewTotal = "crew"
    case passengerTotal = "passengers"
    case cargoCapacity = "cargo_capacity"
    case consumables
    case hyperdriveRating = "hyperdrive_rating"
    case starshipClass = "starship_class"
    case films
  }
}

As with the other data models, you simply list all the response data you want to use, along with any relevant coding keys.

You also want to be able to display information about individual ships, so Starship must conform to Displayable. Add the following at the end of the file:

extension Starship: Displayable {
  var titleLabelText: String {
    name
  }
  
  var subtitleLabelText: String {
    model
  }
  
  var item1: (label: String, value: String) {
    ("MANUFACTURER", manufacturer)
  }
  
  var item2: (label: String, value: String) {
    ("CLASS", starshipClass)
  }
  
  var item3: (label: String, value: String) {
    ("HYPERDRIVE RATING", hyperdriveRating)
  }
  
  var listTitle: String {
    "FILMS"
  }
  
  var listItems: [String] {
    films
  }
}

Just like you did with Film before, this extension allows DetailViewController to get the correct labels and values from the model itself.

Fetching the Starship Data

To fetch the starship data, you’ll need a new networking call. Open DetailViewController.swift and add the following import statement to the top:

import Alamofire

Then at the bottom of the file, add:

extension DetailViewController {
  // 1
  private func fetch<T: Decodable & Displayable>(_ list: [String], of: T.Type) {
    var items: [T] = []
    // 2
    let fetchGroup = DispatchGroup()
    
    // 3
    list.forEach { (url) in
      // 4
      fetchGroup.enter()
      // 5
      AF.request(url).validate().responseDecodable(of: T.self) { (response) in
        if let value = response.value {
          items.append(value)
        }
        // 6
        fetchGroup.leave()
      }
    }
    
    fetchGroup.notify(queue: .main) {
      self.listData = items
      self.listTableView.reloadData()
    }
  }
}

Here is what’s happening in this code:

  1. You may have noticed that Starship contains a list of films, which you’ll want to display. Since both Film and Starship are Displayable, you can write a generic helper to perform the network request. It needs only to know the type of item its fetching so it can properly decode the result.
  2. You need to make multiple calls, one per list item, and these calls will be asynchronous and may return out of order. To handle them, you use a dispatch group so you’re notified when all the calls have completed.
  3. Loop through each item in the list.
  4. Inform the dispatch group that you are entering.
  5. Make an Alamofire request to the starship endpoint, validate the response, and decode the response into an item of the appropriate type.
  6. In the request’s completion handler, inform the dispatch group that you’re leaving.
  7. Once the dispatch group has received a leave() for each enter(), you ensure you’re running on the main queue, save the list to listData and reload the list table view.
Note: You fetch list items asynchronously to allow the total request to finish faster. Imagine you had 100 starships in a movie and had to fetch only one at a time, synchronously. If each request took 100ms, you would have to wait for 10 seconds to fetch all starships! Fetching more starships at the same time greatly reduces this.

Now that you have your helper built, you need to actually fetch the list of starships from a film. Add the following inside your extension:

func fetchList() {
  // 1
  guard let data = data else { return }
  
  // 2
  switch data {
  case is Film:
    fetch(data.listItems, of: Starship.self)
  default:
    print("Unknown type: ", String(describing: type(of: data)))
  }
}

Here’s what this does:

  1. Since data is optional, ensure it’s not nil before doing anything else.
  2. Use the type of data to decide how to invoke your helper method.
  3. If the data is a Film, the associated list is of starships.

Now that you’re able to fetch the starships, you need to be able to display it in your app. That’s what you’ll do in your next step.

Updating Your Table View

In tableView(_:cellForRowAt:), add the following before return cell:

cell.textLabel?.text = listData[indexPath.row].titleLabelText

This code sets the cell’s textLabel with the appropriate title from your list data.

Finally, add the following at the end of viewDidLoad():

fetchList()

Build and run, then tap any film. You’ll see a detail view that’s fully populated with film data and starship data. Neat, right?

Completed film details including starship list

The app is starting to look pretty solid. However, look at the main view controller and notice that there’s a search bar that isn’t working. You want to be able to search for starships by name or model, and you’ll tackle that next.

Sending Parameters With a Request

For the search to work, you need a list of the starships that match the search criteria. To accomplish this, you need to send the search criteria to the endpoint for getting starships.

Earlier, you used the films’ endpoint, https://swapi.dev/api/films, to get the list of films. You can also get a list of all starships with the https://swapi.dev/api/starships endpoint.

Take a look at the endpoint, and you’ll see a response similar to the film’s response:

success({
  count = 37;
  next = "<null>";
  previous = "<null>";
  results =  ({...})
})

The only difference is that this time, the results data is a list of all starships.

Alamofire’s request can accept more than just the URL string that you’ve sent so far. It can also accept an array of key/value pairs as parameters.

The swapi.dev API allows you to send parameters to the starships endpoint to perform a search. To do this, you use a key of search with the search criteria as the value.

But before you dive into that, you need to set up a new model called Starships so that you can decode the response just like you do with the other responses.