Beginning Networking with URLSession

Sep 13 2022 · Swift 5.6, iOS 15, Xcode 13.4.1

Part 2: Download Data

11. Download Music

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 10. Priorities & Cache Policies Next episode: 12. Handle Errors

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 11. Download Music

URLSession - Apple Developer

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

URLSession contains many different tasks to perform a variety of functions. The data task allows you to download content from a web service like the download task, but the data is stored in memory versus being saved to disc. These are really meant for short-lived tasks. Now, you'll explore the download task. Download tasks store the response data in a file. So download task with URL, or URL request and completion handler, gets back a URL instead of a data object. This is the location of a temporary file. So the completion handler must either read and process the data in it. Or copy the file to a permanent location in your app's container directory. You'll see the process of copying files, but if you haven't done any file manipulation in iOS, then check out our Saving Data course. URLSession supports a variety of delegate methods. URLSessionDelegate response to general events. For instance, if your session has been invalidated, or you received an authentication challenge. URLSessionTaskDelegate handles events related to tasks. For instance, if a task is waiting for connectivity, or if a task encountered a redirect. There are also delegates specific for a task. URLSessionDataDelegate handles events related to both the data task and the upload task. For managing downloads, you can make use of URLSessionDownloadDelegate. Use this to handle a variety of situations related to downloads, such as showing progress, and pausing and resuming downloads. Time to write some code and use some of this functionality. Start by opening the starter project for this episode. This time, you'll see that it's a full Xcode project containing an iOS app that you'll extend throughout the course to add more functionality. Build and run the app, which, for now, doesn't really do anything. Don't worry. You'll get things working in just a bit. For now, feel free to take a look at the files that make up the app. You won't go over it again in detail right now, but you'll open up files or create new files as we go along. You're going to download a song from the iTunes preview API. Create a new model file called SongDownloader.swift. Replace the foundation import for a swift UI one. Next, add the following code for the SongDownloader class. Running through this code from top to bottom, you create a class called SongDownloader that conforms to observable object, so you can have it work well with your swift UI views. You create a published URL property for the song's download location. Next up, you create some private properties for the URLSession and URLSession configuration you'll use. Finally, you add an empty initializer for now. Right now, the code gives you an error about not having initialized all stored properties. So let's take care of that. Add this code inside the init method. This sets your property to a default URLSession configuration and creates a new URLSession with your configuration that's stored in a property. Time to move on to actually downloading the song. Start by adding the following method declaration. This is a method that takes a parameter of type URL, which will contain the location from which to download the song. This method does not have a return value, but it's marked async. as it's an asynchronous method you can await. Next, add this code inside your method. Similar to code from previous episode, this code uses your URLSession to perform an asynchronous download from the specified URL. Because this call can throw an error, and it's asynchronous, you use try and await. Should the car return a valid tuple, containing the URL where the downloaded song is at, as well as a response, then you proceed. Otherwise, a print statement will tell you that an error occurred, and you return from the method without doing anything else. Similar to what you also did previously, the response should be checked to ensure it's valid and that things went well. Add this code next. This tries to cast the responses as an HTTP URL response, and check for its status code to be 200. If it isn't, then another print statement is added, and you'll return from the method. You'll work with File Manager to copy the downloaded file from its location into a more permanent location that you prefer. Start by getting the path to your app's documents directory. And then, construct the final URL where you want to store the song. With everything ready to perform the file copy, add the following code. Everything is wrapped inside of do catch statements and some of the methods on File Manager can throw errors. If any occur, you print a message to the console. In your main do block, however, you first check whether the file you are trying to copy doesn't already exist in the desired location. Should there already be such a file, then you remove it first before proceeding with the copy. To actually perform the copy, you call Copy Item on File Manager, and provide it the source and destination URL. Finally, and to wrap up work on this method you need to update your download location property with the new URL where the song file is at. Of importance, and because this is a published property that can potentially cause the UI to update, you wanna make sure you set the property on the main thread. For that, add the following code. This will asynchronously execute your code on the main actor. And that wraps up work on your asynchronous song-downloading method. Time to put it to use. Open SongDetailView.swift. Add the following property towards the top of the strut. This creates an instance of your SongDownloader class, and it uses the observed object attribute so your UI can update whenever its observed state changes. You want something to happen when the Download button is tapped instead of the existing logic that just prints out a message to the console. For that, how about putting the download tapped code inside of a helper function? Inside the SongDetailView struct, add the following. Once again, from top to bottom. You have a private async function. It's async because SongDownloaders download song method is also async. So you wanna keep leveraging the concurrent context it'll execute in when this function is called. Next, you check whether the SongDownloader's download location is there or not. If it isn't, then the song is already downloaded and you can play it. Otherwise, you proceed to call, download song, and await for it to finish downloading the song. To make use of this new function, replace the button in your view's body with the following. Notice how the button's label now intelligently shows either Download or Listen, depending on whether the song has already been downloaded or not. As for the button action itself, you wrap the call to download tapped inside a task, since it's an asynchronous call, and wait for it to complete. The last piece of the puzzle is what to do when the song is already downloaded and the user taps on Listen. Add this code after the padding modifier. Build and run the app, and tap the Download button. Fantastic. The song downloaded and the button's title now reads Listen. Now, tap the Listen button. Yay, great work. Your cooly's doing a lot of things. It's asynchronously downloading an actual song preview from the iTunes API. It gets stored and copied to your app's documents directory, and you can play it back all from within your app. A lot of very cool work in just a few lines of code. One final bit of polish you could do is perhaps give the user feedback that something is happening. Right now my internet connection is fast and working well, but if I or our users were to be on slower connections, then it might seem like the app froze or is doing nothing. Add this property inside the view. This state property will help users know when a download is in progress. Known how it's also annotated using the main actor attribute, so any work on it is guaranteed to be done in the main thread. Since download tab is where the actual download is happening, some updates are needed there as well. If the song isn't downloaded, but before the download begins, use set is downloading to true, and you'll update your UI to leverage this property in a minute. Write below it that the first statement is used to execute some code after download song has finished, and before the method returns. This could have been added after the line that awaits the song download, and also before the guard statement's return. To avoid duplicate code and also potentially forgetting about this and introducing a bug, that the first statement is used. Finally and to wrap all of this up, it's time to update the view to use your new property. Start by changing the button's label. This will check whether a download is in progress, and if so, shows downloading to your users. Next, add the following modifier to the button. This way users can't tap the Download button many times while it's already downloading the song. While not strictly necessary, it can help prevent edge cases and potential bugs. And finally, add a progress view to indicate something is indeed happening. Build and run the app one last time. Yay once more! Now, not only are you downloading the song, but your user interface reflects that, and gives visual feedback to your users about it. Great work, but also great polish and attention to detail. (sighs) That was a lotta work. But, you've seen a different asynchronous transfer that URLSession supports, and you've actually integrated it into a real app. No more playground or sample requests. In the next episode, you'll see how to better handle errors that can happen in SongDownloader's download song method. While printing to the console is a good indicator for you that something went wrong, your users currently have no idea, and might be left frustrated when things don't work. So what are you waiting for? I'll see you in the next episode.