Making a network call in Swift using Alamofire 5

GET | POST

By Will Braynen

Illustration by Donna Blumenfeld

Illustration by Donna Blumenfeld

What’s the weather?

You want to make the following http request to find out what the weather is right now at the visitor center in Sabino Canyon in Tucson, Arizona:

GET https://api.darksky.net/forecast/yourApiKey/32.31,-110.822

Because DarkSky is freaky accurate. Two ways to do this in Swift are: (1) using URLSession, formerly known as NSURLSession, and (2) using Alamofire 5, still in beta as of this writing and quite a bit different from Alamofire 4. In this article, I will show a GET using both: URLSession and Alamofire 5. This side-by-side comparison is handy because the Alamofire 5 migration guide has not yet been written and the Alamofire documentation is currently (as of this writing) out of date during the Alamofire 5 beta process.

If you want to try this yourself and don’t want to 401, you would need your own API key, which you can obtain from DarkSky for free. In the URL above, yourApiKey is your DarkSky API key (while 32.31,-110.822 are latitude and longitude).

With a valid API key and before writing a single line of code, you would next make that GET call using curl or Postman to make sure you got it right. For example, from your macbook’s terminal, you would execute the following command:

$ curl "https://api.darksky.net/forecast/yourApiKey/32.31,-110.822"

Protip: If you pipe your curl into pbcopy (curl url | pbcopy), then you can paste the JSON response into someplace useful.

Note that your API key does not go in the http header here; it goes straight into the URL.

If you receive a sensible response from the server—a nice fat JSON—you would conclude that all looks good and that you are now ready to express your thoughts in code.

The DATA MODEL

Let’s assume that, from the wealth of data you will receive in the JSON response from the server, you are only interested in the following bits:

{
  "timezone": "America/Phoenix",
  "currently": {
    "summary": "Clear",
    "temperature": 46.72,
    "apparentTemperature": 43.96,
    "dewPoint": 18.63,
    "humidity": 0.32,
    "pressure": 1014.91,
    "windSpeed": 5.66,
    "windGust": 12.35,
    "windBearing": 284,
    "cloudCover": 0,
    "uvIndex": 1,
    "visibility": 10,
    "ozone": 315.69
  }
}

In Swift 4, you could then write the following data model:

struct Weather: Codable {
  let timezone: String
  let currently: Currently

  struct Currently: Codable {
    let summary: String
    let temperature, apparentTemperature, dewPoint, humidity: Double
    let pressure, windSpeed, windGust: Double
    let windBearing: Int
    let cloudCover: Double
    let uvIndex: Int
    let visibility, ozone: Double
  }
}

Protip: Instead of writing the model by hand, use this online tool to generate a Codable data model from the JSON you got when you curled the endpoint.

We will presuppose this data model in both methods below: URLSession and Alamofire 5.

The first way: URLSession

If you need to minimize dependencies (e.g. because you are writing a lightweight library), you could use Apple’s good old URLSession:

@discardableResult
func getWeather(
  apiKey: String,
  completion: @escaping (Weather?) -> Void)
  -> URLRequest
{
  let decoder = JSONDecoder()
  let url = "https://api.darksky.net/forecast/\(apiKey)/32.31,-110.822"
  let request = URLRequest(url: URL(string: url)!)

  // Set up the call
  let task = URLSession.shared.dataTask(with: request) { data, response, error in
    // Process the asynchronous response
    if let httpStatus = response as? HTTPURLResponse,
      httpStatus.statusCode == 200,
      let data = data
    {
      // Deserialize the response into the Weather data model
      let weather = try! decoder.decode(Weather.self, from: data)

      // Return the deserialized model to the caller
      completion(weather)
    } else {
      // Call failed, so return nil
      completion(nil)
    }
  }

  // Fire off the call
  task.resume()

  // Not necessary, but might be nice for debugging purposes
  return request
}

The above is something you can even try in Xcode’s Playground. Your call site then would look something like this:

getWeather(
  apiKey: "<INSERT YOUR API KEY HERE>",
  completion: { weather in
    print(weather ?? "no dice")
  }
)

The Second Way: Alamofire 5

But if a dependency on Alamofire is okay, then you could write your GET call using the Alamofire networking library, the younger Swift sibling of the Objective-C AFNetworking library. Using Alamofire 5 (still in beta as of this writing), your call could look like this:

// You might end up with all your pending tasks, including your http request,
// getting canceled if you declare `session` local to getWeather()
let session = Alamofire.Session()

@discardableResult
func getWeather(
  apiKey: String,
  completion: @escaping (DataResponse<Weather>) -> Void)
  -> Request
{
  let url = "https://api.darksky.net/forecast/\(apiKey)/32.31,-110.822"

  // Set up the call and fire it off
  let request = session.request(url).responseDecodable(
    completionHandler: { (response: DataResponse<Weather>) in
      // Process the asynchronous response by returning it to the
      // caller; if successful, response includes a deserialized model
      completion(response)
    }
  )

  // Not necessary, but might be nice for debugging purposes
  return request
}

Protip: print(request) will show you what endpoint the request is for; debugPrint(request) will give you a curl.

Your call site might then look like this:

getWeather(
  apiKey: "ec89da681e7f8f76a0d14e89544142fd",
  completion: { response in
    if response.result.isSuccess,
      let weather = response.result.value
    {
      print(weather)
    } else {
      // `response.error?.asAFError` has more information
      print("no dice")
    }
  }
)

You would obviously need to add “import Alamofire” at the top of your file and would need to install Alamofire 5 (“5.0.0.beta.1” is the latest git tag at the time of this writing) using the dependency manager of your choice (e.g. CocoaPods or Swift Package Manager).

Happy networking!