avatarTobias Wissmueller

Summary

This article provides an introduction to asynchronous programming in Swift, with a focus on sending HTTP GET and POST requests using the async and await keywords, and demonstrates practical code examples for Xcode Playgrounds.

Abstract

The article begins by explaining the concept of asynchronous code execution in Swift, emphasizing the importance of the async and await keywords for managing long-running tasks without blocking the main thread. It then proceeds to offer two detailed examples: the first demonstrates how to perform an HTTP GET request, including how to handle the response and decode JSON data. The second example builds upon the first by illustrating an HTTP POST request with a JSON payload, discussing the encoding of data into JSON format. The author provides insights into Swift's concurrency model, the deprecation of certain asynchronous patterns in favor of Task.init, and the use of Swift's Codable protocol for data serialization. The article concludes by inviting readers to experiment with the provided code snippets in Xcode Playgrounds and encourages engagement with the author's Medium content.

Opinions

  • The author believes that understanding asynchronous programming is crucial for modern Swift development, particularly for tasks like network requests.
  • The examples provided are considered practical and directly applicable to real-life scenarios, enhancing the reader's learning experience.
  • The article suggests that the Swift community should adapt to the evolving concurrency model, including the shift from deprecated async to Task.init.
  • Encouragement to follow the author on Medium and support their work indicates the author's desire to build a community and establish thought leadership in the field of Swift programming.
  • The inclusion of resources for further learning, such as Apple's developer videos and documentation, shows the author's commitment to comprehensive education and self-improvement for developers.

Sending Asynchronous HTTP-Requests in Swift

A Practical Introduction to Task and Await

Photo by Jess Bailey on Unsplash

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!

Resources

Swift
Asynchronous
Tasks
Await
Asynchronous Programming
Recommended from ReadMedium