How to Make an HTTP Request in Swift

Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

There are plenty of third party libraries you can use to perform HTTP requests in Swift, but I always default to Foundation's URLSession API. It is a first party API and easy to use. The fewer dependencies a project has the better. In this post, you learn how easy it is to perform an HTTP request in Swift using the URLSession API. I show you how to fetch a remote image and JSON data.

Creating a Playground

Fire up Xcode and create a playground by selecting New > Playground... from Xcode's File menu. Choose the Blank template from the iOS > Playground section.

How to Make an HTTP Request in Swift

Remove the contents of the playground with the exception of the import statement for the UIKit framework at the top. The URLSession class is defined in the Foundation framework. Importing UIKit automatically imports Foundation.

import UIKit

Working with URLSession

URLSession replaces NSURLConnection. The URLSession API is easier to use and modern. It is available on iOS, tvOS, macOS, and watchOS. URLSession is one class of the URLSession API. There are a handful of types you need to become familiar with to perform an HTTP request in Swift.

A URLSession instance is the manager or coordinator of the requests your application performs. A request is referred to as a task, an instance of the URLSessionTask. You never directly use the URLSessionTask class. Foundation defines a number of URLSessionTask subclasses. Each subclass has a specific objective, such as downloading or uploading data.

Creating a Data Task

Let's use the URLSession API to perform an HTTP request. The objective is to fetch the data for an image. Before we start, we need to define the URL of the remote image.

import UIKit

let url = URL(string: "https://bit.ly/2LMtByx")!

The next step is creating a data task, an instance of the URLSessionDataTask class. A task is always tied to a URLSession instance. To keep things simple, we ask the URLSession class for the shared singleton session object through its shared class property.

URLSession.shared

We then ask the URLSession instance to create a data task by invoking the dataTask(with:completionHandler:) method. This method returns a URLSessionDataTask instance and accepts two arguments, a URL object and a completion handler. The completion handler, a closure, is executed when the data task completes, successfully or unsuccessfully. The completion handler accepts three arguments, an optional Data object, an optional URLResponse object, and an optional Error object.

import UIKit

let url = URL(string: "https://bit.ly/2LMtByx")!

let task = URLSession.shared.dataTask(with: url) { data, response, error in

}

A data task fails or succeeds. If the data task fails, then error has a value. If the data task succeeds, then data and response have a value. We are not interested in the URLResponse object for now. We safely unwrap the Data object and use it to create a UIImage instance. If data is equal to nil, then the HTTP request failed and we print the value of error to the console.

import UIKit

let url = URL(string: "https://bit.ly/2LMtByx")!

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let image = UIImage(data: data)
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }
}

Even though we created a data task, the HTTP request isn't executed. We need to call resume() on the URLSessionDataTask instance to execute it.

import UIKit

let url = URL(string: "https://bit.ly/2LMtByx")!

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let image = UIImage(data: data)
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }
}

task.resume()

If you run the contents of the playground and wait a few seconds, you should see the result of the HTTP request appear in the results panel on the right. The dimensions of the UIImage instance are displayed in the results panel. No error is printed to the console, which means the HTTP request was successfully executed.

Fetching a Remote Image in Swift

Creating a Request

We created a data task by passing a URL object to the dataTask(with:completionHandler:) method. This works fine, but it doesn't offer a lot of flexibility. The URLSession class defines another method that accepts a URLRequest object. As the name suggests, the URLRequest struct encapsulates the information the URL session needs to perform the HTTP request. Let me show you how this works.

We create a URLRequest object by passing the URL object to the initializer and store the result in a variable with name request. We modify the URLRequest object in a moment hence var instead of let.

var request = URLRequest(url: url)

We pass the URLRequest object to the dataTask(with:completionHandler:) method. The name of the method is similar to the one we used earlier. The difference is that it accepts a URLRequest object instead of a URL object.

import UIKit

let url = URL(string: "https://bit.ly/2LMtByx")!

var request = URLRequest(url: url)

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let image = UIImage(data: data)
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }
}

task.resume()

Execute the contents of the playground. Notice that the result is identical. You may wonder what we gain by using URLRequest. The URLRequest object allows us to configure the HTTP request the URL session performs. We can set the HTTP method, through the httpMethod property. This isn't necessary in this example since it defaults to GET.

request.httpMethod = "GET"

We can also define the request's HTTP header fields. If you need to override the default HTTP header fields, then you set the allHTTPHeaderFields property, a dictionary of type [String:String]. This is also important if you need to deal with authentication. In this example, we add an HTTP header field to pass an API key to the server we are communicating with.

request.allHTTPHeaderFields = [
    "X-API-Key": "123456789"
]

You can also set the request's HTTP header fields by invoking the setValue(_:forHTTPHeaderField:) method. This is a mutating method that sets the value for a given HTTP header field.

request.setValue("application/png", forHTTPHeaderField: "Content-Type")

Requesting JSON with URLSession

Fetching JSON is another common example. This isn't any more difficult than fetching the data for a remote image. We update the URL object and set the value of the Content-Type HTTP header field to application/json.

import UIKit

let url = URL(string: "https://bit.ly/3sspdFO")!

var request = URLRequest(url: url)

request.setValue("application/json", forHTTPHeaderField: "Content-Type")

We have a few options to decode the response. I don't recommend the JSONSerialization class unless you have a very good reason. The preferred and recommended approach is the Decodable protocol in combination with the JSONDecoder class.

We define a struct with name Book that conforms to the Decodable protocol. The Book struct defines two properties, title of type String and author of type String.

import UIKit

struct Book: Decodable {

    // MARK: - Properties

    let title: String
    let author: String

}

let url = URL(string: "https://bit.ly/3sspdFO")!

var request = URLRequest(url: url)

request.setValue("application/json", forHTTPHeaderField: "Content-Type")

In the completion handler, we create a JSONDecoder instance and invoke the decode(_:from:) method, passing in the type of the value to decode from the supplied JSON object and the JSON object to decode. We print the result to the console. Execute the contents of the playground to see the result.

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        if let books = try? JSONDecoder().decode([Book].self, from: data) {
            print(books)
        } else {
            print("Invalid Response")
        }
    } else if let error = error {
        print("HTTP Request Failed \(error)")
    }
}

Scratching the Surface

This post has taught you how to perform an HTTP request in Swift using the URLSession API. I hope you agree that this isn't rocket science. The URLSession API is easy to use and, if you learn more about the API, flexible and powerful. We only scratched the surface in this tutorial.

Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy