Using URLSession to retrieve JSON in Swift (2) — With Unsplash API

Photo by Josh Carter on Unsplash

This article is to demonstrate how to retrieve JSON data from a HTTP Web API in iOS development continuing Part 1 with Swift 4. To present a more practical example, we will fetch data from Unsplash API. And we will cover most commonly seen type of JSON response.

The source code of this tutorial is available on Github.

Let’s start the journey. Hope you enjoy it!


Register an account

First, we are going to public API provided by Unsplash. Unsplash offers a lot of free-to-use extraordinary images by a community of brillant photographers. The heading image I use for this post is also from Unsplash provided in Medium. And we are going to get some random images now.

As the official documentation states, join as a developer before we can make use of the APIs from Unsplash. Make sure you have a complete understanding on the terms and guidelines on the services provided before registration.


Let’s Get Started

In this tutorial, we will also stick to Xcode storyboard as we did in the last one. Create a new one if you haven’t and insert the following code at the top.

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

From the document, we know that the base url is:

https://api.unsplash.com/

And we are going to make an extension of the URL for better use.

extension URL {
private static var baseUrl: String {
return "https://api.unsplash.com/"
}

static func with(string: String) -> URL? {
return URL(string: "\(baseUrl)\(string)")
}
}

Just like last tutorial, we going to make URLs to be executed by URLSession.

By creating an applications in your Unsplash account, you should be able to obtain your Access Key.

This time we need to insert a header containing the Access Key to our requests with URLRequest to get a random photo with reference to Get a random photo as follows, replacing the YOUR_ACCESS_KEY:

if let url = URL.with(string: "photos/random") {
var urlRequest = URLRequest(url: url)
urlRequest.setValue("Client-ID YOUR_ACCESS_KEY", forHTTPHeaderField: "Authorization")

// ... URLSession
}

Then we use URLSession to execute the urlRequest.

URLSession.shared.dataTask(with: urlRequest) { data, response, error in 
if let data = data {
if let jsonString = String(data: data, encoding: .utf8) {
print(jsonString)
}
}
}.resume()

Then you should successfully see a very long JSON string containing information of a random image.

{"errors":["OAuth error: The access token is invalid"]}

But if you see error line like above, make sure you have entered the Access Key correctly in the header field.


Decode the JSON

Getting the JSON string is not useful. We have to convert it into a model. With reference to the image response object, we declare the struct as below.

struct Image: Decodable {
let id: String
let width: Int
let height: Int
let color: String
}

To make it simple first, we only declare a few keys of the Image object.

URLSession.shared.dataTask(with: urlRequest) { data, response, error in 
if let data = data {
do {
let image = try JSONDecoder().decode(Image.self, from: data)
print(image)
} catch let error {
print(error)
}
}
}.resume()

And you should see the image with data is printed.

Image(id: "Dwu85P9SOIk", width: 2448, height: 3264, likes: 24, color: "#6E633A")

But that is not enough! We only have the keys with primitive type String and Int parsed. What about the other key-values? say the image urls?


Decode nested JSON

For a real application like this you probably will encouter a more complicated JSON. Luckily it is also easy. Just make sure every properties of the struct are Codable. And obviously primitive types like String and Int already are. So we are creating one more struct conforming to Decodable.

struct URLs: Decodable {
let raw: String
let full: String
let regular: String
let small: String
let thumb: String
}

And add to the Image struct.

struct Image: Decodable {
// ...
let urls: URLs
}

And you should get the urls as the value is an object.

let image = try JSONDecoder().decode(Image.self, from: data) 

print(image.urls)

// URLs(raw: "https://images.unsplash.com/photo-1417325384643-aac51acc9e5d", full: "...", regular: "...", small: "...", thumb: "...")

There is also a case that the property is an array of something like current_user_collections in the response image JSON.

We can declare the struct as below to cater for the situation.

struct CurrentUserLocation: Decodable {
let id: Int
let title: String
// ...
}

struct Image: Decodable {
// ...
let current_user_collections: [CurrentUserLocation]
}

Decode array JSON

There is also another case that the JSON object returned is in itself an array. As the Unsplash random image API states, if you request the count parameter in the request. The response will become an array of images instead of the image itself.

if let url = URL.with(string: "photos/random?count=2") {
// ...
}

And you can easily decode the array of images like below.

do {
let images = try JSONDecoder().decode([Image].self, from: data)
print(images) // get an array of image object
} catch let error {
print(error)
}

For more information about Codable, as I have mentioned, Apple has a very good read on custom types. You do not want to miss it.

Decode a JSON that the key-value may not exist

Often times we will find the JSON response may or may not contain certain key-value for certain cases. Then you have to declare the key as Optional.

struct Image: Decodable {
let id: String
let width: Int
let height: Int
let color: String
let keyNotExist: String?
}

And, if the key-value does not exist, you should see the image object is printed as:

Image(id: "Dwu85P9SOIk", width: 2448, height: 3264, likes: 24, color: "#6E633A", keyNotExist: nil)

Otherwise, it will parse to an Optional String in this case.

Image(id: "Dwu85P9SOIk", width: 2448, height: 3264, likes: 24, color: "#6E633A", keyNotExist: Optional("some string"))

This is the end of the tutorial. I am sure that by knowing these basic usage of URLSession, you are able to deal with most of the practical usage of retriving HTTP API responses in iOS development with Swift.

The source code of this tutorial is available on Github.

Hope you find this useful :).

Have a nice day!


Messages are welcome. You can find me on LinkedIn.

Like what you read? Give Calvin Wong a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.