Leave a rating/review
Notes: 06. Get Data from a Session
URLSessionConfiguration - Apple Developer URLSession - Apple Developer
Welcome back! The URLSession API has many moving part, centered on the URLSession class itself.
As you know, to create a session, you need an instance of URLSessionConfiguration.
You then use the session to create URLSessionTask instances to transfer data to or from a server on the network. It’s much more efficient to create multiple tasks in a session and not a session for every task.
You can create a task with a closure-based completions to handle the server’s response, but you can also use the new methods on URLSession that leverage Swift’s concurrency functionality in order to perform asynchronous network transfers.
To monitor progress or handle authentication challenges you can implement delegate methods.
There are three concrete subclasses of URLSessionTask: 1. URLSessionDataTask 2. URLSessionUploadTask 3. And URLSessionDownloadTask
A data task returns the response as an object in memory.
An upload task is very similar to a data task, but makes it easier to provide a request body.
A download task doesn’t return the response in memory, but writes the data to a file and returns the location of the file. All three types are similar in that they supply some data to the server as a request and receive some data from the server as a response.
For this demo, you’ll use an asynchronous method to get data from a service which you can then transfer into JSON.
An important note about tasks, not for when using the asynchronous APIs of URLSession: when you start a task, they are created in a waiting state. To start the task, you must call the resume method.
One of the biggest errors when working with non-async tasks is to create the task and forget to call resume. When you start debugging some code because it isn’t returning data, the first place you should check is to see if you resumed a task.
In this course, you’ll be querying the iTunes API. It’s a great way to download and play around with live JSON data. To get started, create a new playground.
Next, remove all the contents and add an import for SwiftUI:
import SwiftUI
Start by getting the default configuration:
let configuration = URLSessionConfiguration.default
And then create a new session using the configuration:
let session = URLSession(configuration: configuration)
Create the URL used to make the request:
guard let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=starlight") else {
fatalError("Could not create the URL.")
}
This URL is telling the iTunes search API to look for music, specifically songs, and to use the search term starlight.
Okay, now you need to acquire the data from its origin. You want to fetch the JSON from the service specified in the URL and ultimately print out the contents to the console. You do this with the data from URL method:
let (data, response) = try await session.data(from: url)
A lot of the magic is happening thanks to Swift’s concurrency functionality, but there’s an error. This is because you’re calling an asynchronous method in a non-asynchronous function, or outside of a Task. Fix it by updating your code:
Task {
let (data, response) = try await session.data(from: url)
}
Let’s go over what this single line of code is doing. Because this method can throw an error, you call it with the try keyword, and because it’s an asynchronous method, you use await to wait until the session has finished transferring and receiving data.
If you’ve used the closure-based APIs of URLSession before, this code probably looks very simple and streamlined in comparison.
It’ll also make your code easier to read in the long-term as you don’t have to deal with a lot of closures or nested closures.
Note that URLSession and its APIs are thread-safe, meaning you can create sessions, transfers and tasks in any thread or background context.
Going back to the returned tuple: data is the actual data being retrieved from the server. response is a URLResponse, but if you make an HTTP request like in this example, the returned object is actually an HTTP response.
To handle the response and see if it was valid and successful, add this code at the end of your Task:
guard let httpResponse = response as? HTTPURLResponse,
(200..<300).contains(httpResponse.statusCode)
else {
return
}
You first cast the response into an HTTPURLResponse object and then check the status code. In this case, you’re just checking to make sure you received a code in the HTTP 200 range, meaning everything went well.
There are lots of other status codes out there like 404 for page not found, 500s for server error, and more. Check what works best for your app. Next, check if the received data is valid:
guard let httpResponse = response as? HTTPURLResponse,
(200..<300).contains(httpResponse.statusCode),
let dataString = String(data: data, encoding: .utf8)
else {
return
}
You take the data and convert it to a string, using a helper method on String itself. Finally, print out the result:
print(dataString)
Run the playground and voila. You made a web request and got back some JSON in just a few lines of code.
Better still, you get all the bells and whistles of the network framework ensuring your users have a pleasant experience no matter what type of network they are using.
If coming from the closure-based APIs of URLSession, notice how you didn’t have to manually call resume in order to start your download. The async APIs on URLSession will simply throw an error, or return to you once the task is done.
Awesome. With just a few lines of code, sprinkled with some modern Swift concurrency, you’ve managed to query the iTunes search API to download data from a real resource on the internet!
In the next episode, a challenge episode, you’ll put this new knowledge to the test in order to download some data from the network and then display it using SwiftUI.
So get ready and I’ll see ya in a minute! :)