Skip to main content

Sending Asynchronous HTTP-Requests in Swift

·5 mins

A Practical Introduction to Task and Await #

This post is an introduction on how to asynchronously execute code in Swift. First, a general view on the topic will be provided. After that, two concrete code examples will demonstrate on how to use asynchronous code in a practical real-life example.

The code examples have been created for Xcode Playgrounds and can directly be copied, pasted and run.

Both requests will run against reqbin.com which is an online REST and SOAP API testing tool.

In the first example, the HTTP GET request will be used, which can also be tried out online on their website.

Then, as the second example, the HTTP POST request will be used with a JSON payload.

The post will also cover a bit on how to encode and decode data to JSON.

Asynchronous Code Execution #

Before we get into the detail of how to send HTTP requests, let’s have a look at the asynchronous code execution.

The Swift documentation says the following about “Asynchronous Code”:

Asynchronous code can be suspended and resumed later, although only one piece of the program executes at a time. Suspending and resuming code in your program lets it continue to make progress on short-term operations like updating its UI while continuing to work on long-running operations like fetching data over the network or parsing files.

To make a long-running function asynchronous, we need to mark it with the async-keyword.

func myLongRunningFunction() async {
	...
  // do something that takes lots of time
  ...
}

Then, in order to call this function, we need to use the await- keyword.

await myLongRunningFunction()

Let’s take a specific example of Task.sleep(). In the code below we want to wait 5 seconds.

print("Before")
try Task.sleep(nanoseconds: 5 * 1_000_000_000)
print("Done")
print("After")

This code cannot be compiled. We get the error message: “’async’ call in a function that does not support concurrency”.

Because the main-thread of our Xcode Playground is synchronous we need to call async beforehand.

print("Before")
async {
	try await Task.sleep(nanoseconds: 5 * 1_000_000_000)
	print("Done")
}
print("After")

While this code is running just fine, it will just be a matter of time until it won’t compile any more.

The reason for this is, that async is deprecated and will be replaced by Task.init.

Let’s quickly change that.

print("Before")
Task {
	try await Task.sleep(nanoseconds: 5 * 1_000_000_000)
	print("Done")
}
print("After")

Running this will result in the following output.

Before
After
Done

Whereas the “Before” and “After” will be printed immediately. It takes 5 seconds for “Done” to be printed.

Now, that we got the basic picture on asynchronous code execution, let us have a look on how to use that for sending HTTP requests.

Sending HTTP GET Request #

Here is the example code for sending a HTTP GET request.

Basically we need to do three steps:

  • Create the request with the URL
  • Send the request
  • Evaluate the response
import PlaygroundSupport
import UIKit

struct SuccessInfo: Decodable {
    let success: String
}

guard let url = URL(string: "https://reqbin.com/echo/get/json") else { fatalError("Missing URL") }

var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")

Task {
    let (data, response) = try await URLSession.shared.data(for: urlRequest)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }

    let successInfo = try JSONDecoder().decode(SuccessInfo.self, from: data)

    print(String(data: data, encoding: .utf8) ?? "default value")
    print("Success: \(successInfo.success)")
}

print("I am here already!")

PlaygroundPage.current.needsIndefiniteExecution = true

The important classes are:

“A value that identifies the location of a resource, such as an item on a remote server or the path to a local file.”

URLRequest

“A URL load request that is independent of protocol or URL scheme.”

“An object that coordinates a group of related, network data transfer tasks.”

“The metadata associated with the response to an HTTP protocol URL load request.”

Sending HTTP POST Request #

This section shows the HTTP POST request with a JSON payload. Again, we need to perform the same three steps as in the first example.

The big difference in this example is, that we send some data to the server.

Our data is encoded in the MyJsonObject. You can use whatever payload you want, the backend itself just ignores it.

Most REST services respond with the same resource type, therefore we could have used MyJsonObject as the only type and let it inherit from Codable.

import PlaygroundSupport
import UIKit

struct MyJsonObject: Encodable {
    let id: Int
    let foo: String
    let bar: String
}

struct SuccessInfo: Decodable {
    let success: String
}

let myJsonObject = MyJsonObject(id: 1, foo: "Hello", bar: "World")
let payload = try JSONEncoder().encode(myJsonObject)

guard let url = URL(string: "https://reqbin.com/echo/post/json") else { fatalError("Missing URL") }

var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = "POST"

Task {
    let (data, response) = try await URLSession.shared.upload(for: urlRequest, from: payload)

    guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }

    let successInfo = try JSONDecoder().decode(SuccessInfo.self, from: data)

    print(String(data: data, encoding: .utf8) ?? "default value")
    print("Success: \(successInfo.success)")
}

print("I am here already!")

PlaygroundPage.current.needsIndefiniteExecution = true

Let’s have a look and find out what the difference between an Encodable, Decodable and Codable is.

“An object that decodes instances of a data type from JSON objects.”

“A type that can encode itself to an external representation.”

“A type that can decode itself from an external representation.”

“A type that can convert itself into and out of an external representation.”

Running the Code #

When executing the code from the examples you might see, that the line with “I am here already!” is being printed before the HTTP-response body.

Conclusion #

After a general introduction on how to write asynchronous code in Swift with async and await, two practical examples have been provided on how to asynchronously send and receive data via HTTP GET and POST requests.

Feel free to copy and paste the code into your own Xcode-Playground and play around with it.

Thank you for reading!

https://twissmueller.medium.com/membership

Resources #