Networking
Most apps these days need a way to download data from the Internet — whether that’s files, images, or by talking to some form of web API through a format like JSON. While there are a multitude of solutions and frameworks available to us when it comes to implementing networking in Swift — let’s take a look at how to use Foundation’s URLSession
API to load data over the network.
It all starts with a URL. While the Swift standard library’s URL
type allows us to create a URL value using a string, a more robust option is usually to use URLComponents
. That’ll let us construct the various parts of a URL in steps, and then use the components’ url
property to build the URL we need:
// Construct a URL by assigning its parts to a URLComponents value
var components = URLComponents()
components.scheme = "https"
components.host = "api.github.com"
components.path = "/users/johnsundell"
// This will give us the constructed URL as an optional
let url = components.url
Above we take the URL to use when requesting my GitHub profile data through their web API, and break it down into its scheme
, host
(the base URL of the server), and the path
we’re interested in — and assign those values to an instance of URLComponents
to have it construct our URL.
Next, let’s use our newly constructed URL to perform a network request. To do that we’ll use our app’s default URLSession
to create a data task, and supply a completion closure that we wish to be called once the data task will be completed:
guard let url = components.url else {
preconditionFailure("Failed to construct URL")
}
let task = URLSession.shared.dataTask(with: url) {
data, response, error in
}
As you can see above, URLSession
will send three arguments to our data task’s completion closure once finished:
data
: Will either contain the bytes that were downloaded, ornil
if an error occurred.response
: A representation of the response that was received. Contains things like the MIME type and encoding of the downloaded data, and can be type casted toHTTPURLResponse
(if we wish to get more HTTP-specific info, like the status code).error
: Any error that was encountered, ornil
if the operation was a success.
Let’s say that we want to either take the data that we downloaded and render it as a string to a UILabel
, or — in the case of an error — use the same label to show a description of the error that was encountered. To do that we’ll first unwrap the data
optional, and if that fails we’ll instead use the localizedDescription
of the passed error — like this:
if let data = data {
label.text = String(decoding: data, as: UTF8.self)
} else {
label.text = error?.localizedDescription
}
However, since our data task will execute asynchronously on a background thread (we wouldn’t want our entire app to pause while we’re waiting for a network request to finish) — we need to use Grand Central Dispatch to dispatch our UI update onto the main queue, since UIKit cannot be used from a background queue:
DispatchQueue.main.async {
if let data = data {
label.text = String(decoding: data, as: UTF8.self)
} else {
label.text = error?.localizedDescription
}
}
Finally, to start our data task and actually perform our network request, we need to call resume()
on it:
task.resume()
And that’s it! 🎉 Here’s a complete example, which when run will either display a JSON representation of my GitHub profile, or a description of any error that was encountered, using the label we created:
var components = URLComponents()
components.scheme = "https"
components.host = "api.github.com"
components.path = "/users/johnsundell"
guard let url = components.url else {
preconditionFailure("Failed to construct URL")
}
let label = UILabel()
let task = URLSession.shared.dataTask(with: url) {
data, response, error in
DispatchQueue.main.async {
if let data = data {
label.text = String(decoding: data, as: UTF8.self)
} else {
label.text = error?.localizedDescription
}
}
}
task.resume()
While implementing a complete networking solution might involve several other tasks — such as posting data using the POST
method (which can be done using the same technique as above, but by using a URLRequest
instead of a plain URL
), customizing caching behavior, and much more — using URLSession
like we did above can be a great way to start.
Thanks for reading! 🚀