Using URLSession’s async/await-powered APIs
One of the major new features that are being introduced in Swift 5.5 is async/await, and as part of their latest SDKs, Apple have also updated many of their built-in asynchronous APIs to take advantage of this new feature.
In this article, let’s take a look at URLSession
specifically, and how we can now use the async/await pattern to perform tasks such as data fetches, downloads and uploads.
Loading some form of Data
using a standard GET
request is arguably the most common type of network call that apps perform on a regular basis, and using async/await, this can now be done using just a single line of code:
let (data, response) = try await URLSession.shared.data(from: url)
Apart from the new await
keyword, the above call uses the standard try
keyword to indicate that an error might be thrown, and then stores the result using a deconstructed tuple that’s made up of two parts — the data
that was downloaded, and the response
that was received.
Taking things a bit further, here’s how we could use the above new API to first load a set of JSON data from a given URL, and then decode that data using Swift’s built-in Codable API:
struct ItemLoader {
var session = URLSession.shared
func loadItems(from url: URL) async throws -> [Item] {
let (data, _) = try await session.data(from: url)
let decoder = JSONDecoder()
return try decoder.decode([Item].self, from: data)
}
}
Note how we’re currently using an underscore to ignore the response
value that was returned from our asynchronous call, which is something that can be done in contexts where we don’t wish to handle the response itself in any way.
Since we’re calling an asynchronous method within loadItems
, that method itself also needs to be marked with the async
keyword, and we can once again use try
(this time in combination with marking our method with the throws
keyword) to propagate any error that was encountered.
Downloading files
Next, let’s take a look at how we can also use the async/await pattern to perform file downloads. The difference between such requests and the data
API that we just took a look at above is that download tasks write their results to disk, rather than storing them as Data
values in memory, which is especially useful for larger pieces of data, such as files.
So when calling the download
API (which can once again be done using the new await
keyword), we get back a localURL
at which the downloaded file was stored, rather than a Data
representation of its contents:
struct FileLoader {
var session = URLSession.shared
func downloadFile(from url: URL) async throws -> File {
let (localURL, _) = try await session.download(from: url)
return File(url: localURL)
}
}
We could of course have returned our localURL
value as-is, but in this case, we’re wrapping it within a simple File
struct to clearly indicate that we’re actually returning a reference to a file.
Uploading files
To perform an upload, we can’t simply use a plain URL
as our destination, but must instead pass a URLRequest
instance that contains additional metadata, for example what HTTP method that we wish to use.
In this example we’re using a POST
request to upload a piece of Data
to a given URL, and we’re then returning the URLResponse
that was received:
struct DataUploader {
var session = URLSession.shared
func upload(_ data: Data, to url: URL) async throws -> URLResponse {
var request = URLRequest(url: url)
request.httpMethod = "POST"
let (responseData, response) = try await session.upload(
for: request,
from: data
)
return response
}
}
Note how we probably should’ve ignored the responseData
value using the same underscore technique that we used earlier (given that we’re not actually using that value), but in this example I thought it was important to include it for additional clarity.
Conclusion
So that’s three different ways to use async/await with URLSession
. In upcoming articles, we’ll explore this new pattern further, and we’ll also take a look at how we can connect async
calls to things like SwiftUI views.
For more information about URLSession
and these new APIs, make sure to check out Apple’s official documentation.
Thanks for reading!
Easily build and manage iOS and Android in-app purchases. With just a few lines of code RevenueCat provides IAP infrastructure, customer analytics, data integrations, and gives you time back from dealing with edge cases and updates across all platforms.