Leave a rating/review
Notes: 07. Challenge: Fetch Data Over the Network
URLSessionConfiguration - Apple Developer URLSession - Apple Developer
Welcome to the seventh episode! Time for a little challenge for you.
In this challenge, you’ll take a basic Swift playground that contains some model code and SwiftUI view code in order to download data from the iTunes search API, parse it (or decode it as it’s often referred to) and even display part of the data in your user interface.
To get started, open this episode’s Starter playground. The main playground page just sets up the playground to show your SwiftUI view. Switch over to MediaView.swift and look at the contents.
This view sets up a button that makes the network request to search the iTunes API and shows the results in a List.
Towards the bottom of the file there is an async method with some boilerplate code for you to get started with your data download.
Note also that this method is called inside the button’s action using a Task, since the method awaits as it’s marked async.
Moving on, open MediaResponse.swift. This file contains a couple of structs modeled after the iTunes search API response. Your goal is to make the iTunes search API request, parse the results into the model objects and show them in your list.
Remember that you can reference and look at the previous episode’s code and use JSONDecoder in order to parse the response into your MediaResponse object as opposed to a String, like you did in the previous episode. :) Good luck! :)
Welcome back. How did it go? Let’s go over how you could have solved the challenge.
Open MediaView.swift and add this code where the TODO was:
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
Next, create a Task where the URLSession’s method will execute:
Task {
}
Inside the task, make the actual call to download the URL’s contents:
Task {
let (data, response) = try await session.data(from: url)
}
Similar to the previous episode, validate your response or throw an error otherwise:
guard let httpResponse = response as? HTTPURLResponse,
(200..<300).contains(httpResponse.statusCode)
else {
throw MediaError.requestFailed
}
Next, and referencing the commented out code at the bottom of the method, which you can now delete, add the following at the bottom of your task:
guard let mediaResponse = try? JSONDecoder().decode(MediaResponse.self, from: data) else {
throw MediaError.responseDecodingFailed
}
This code uses JSONDecoder to parse the response into an object of type MediaResponse. Just as when validating the response, if decoding fails then you throw an error.
Finally, you update your musicItems property in order to update the user interface and list out the response items:
await MainActor.run {
musicItems = mediaResponse.results
}
As we discussed in earlier episodes, all UI updates must be performed on the main thread. Because musicItems is a State property that’s managed by SwiftUI, and thus will trigger a UI update, setting its value must be done on the main thread in order to avoid potential issues.
And that’s one of the things to keep in mind about concurrency, you may have forgotten to do this on the main thread and yet nothing crasher or went wrong, but that doesn’t mean that the code isn’t without problems.
Great, great work! This was definitely a challenging…challenge, heh.
You used a LOT of different concepts and knowledge to get to your goal, so kudos to you for making it all the way to the end.