Typesafe API calls in Swift: Generating Alamofire Handlers with quicktype
Alamofire is an elegant HTTP networking library that makes working with JSON APIs in Swift a breeze. quicktype makes Alamofire even more awesome by generating extensions and Codables for typesafe API calls.
Here's Alamofire's canonical example of handling a JSON API response:
Alamofire.request("https://httpbin.org/get").responseJSON { response in
if let json = response.result.value {
// json is of type Any, so we must cast and use dictionary indexers
let data = json as! [String: Any]
let headers = data["headers"] as! [String: String]
print(headers["Accept-Language"])
}
}
Unfortunately, as you can see, you're still left with json: Any
, a dynamic value that you must cast and awkwardly introspect for the data you care about.
quicktype can generate statically-typed Alamofire response handlers along with Codable
structs from JSON, allowing us to rewrite the above sample like this:
Alamofire.request("https://httpbin.org/get").responseHTTPBinGet { response in
if let json = response.result.value {
print(json.headers.acceptLanguage)
}
}
How it's done
You can use the quicktype CLI or app.quicktype.io to generate Alamofire extensions and Codable structs directly from the API endpoint:
$ quicktype https://httpbin.org/get --alamofire -o HTTPBinGet.swift
quicktype generates the following Codable
type for this API:
struct HTTPBinGet: Codable {
let args: Args
let headers: Headers
let origin, url: String
}
struct Args: Codable {
}
struct Headers: Codable {
let accept, acceptEncoding, acceptLanguage, connection: String
let host, upgradeInsecureRequests, userAgent: String
enum CodingKeys: String, CodingKey {
case accept = "Accept"
case acceptEncoding = "Accept-Encoding"
case acceptLanguage = "Accept-Language"
case connection = "Connection"
case host = "Host"
case upgradeInsecureRequests = "Upgrade-Insecure-Requests"
case userAgent = "User-Agent"
}
}
And Alamofire extensions to handle the API response:
extension DataRequest {
// ...
@discardableResult
func responseHTTPBinGet(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<HTTPBinGet>) -> Void)
-> Self {
return responseDecodable(queue: queue, completionHandler: completionHandler)
}
}
The code generated by quicktype allows you to do a statically-typed Alamofire request, getting the benefit of type-safety and code completion on your response data!
Alamofire.request("https://httpbin.org/get").responseHTTPBinGet { response in
if let json = response.result.value {
* // json is an instance of HTTPBinGet
print(json.headers.acceptLanguage)
}
}