GET | POST
By Will Braynen
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!