Beginning Networking with URLSession

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

Part 2: Download Data

17. Pause, Resume & Cancel Downloads

Episode complete

Play next episode

About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 16. Simulate Different Network Speeds Next episode: 18. Conclusion

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: 17. Pause, Resume & Cancel Downloads

URLSessionConfiguration - Apple Developer URLSession - Apple Developer

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

When users start to download a file, they may later change their mind about the download. The file may be too large, or their connection is too slow. Or maybe they need to pause the download, so they can download another file first. Thankfully, this is all really easy to do. The thing is that this will use URL session tasks, and not the asynchronous transfer methods. No worries, I'll show you how to do that in a bit. To cancel a download, you need to call the cancel method on a task. That's it. The download stops. Pausing and resuming is a little bit different. To pause, you call cancel by producing resume data. This cancels the task, but you'll receive a data object. This data object contains the resume data for your file. The file itself still exists in a temporary location on disc. You can do whatever you want with the resume data. For instance, you may wanna save it to disc until the next time the user wants to restart the download. Later, when you wanna resume the download, you need to create a new task, passing in the data. You call the URL session method, download task with resume data, you pass in that data, and the download will continue. Just keep in mind that there are a few stipulations when it comes to resuming downloads. First, you won't be able to resume downloading if the resource has changed, meaning if the original file receives an update or has since been removed. Next, the download must have originated from an HTTP or HTTPS get request. The server must also support either an e-tag or a last modified Heather in its response and must support byte range requests. Finally, the temporary downloaded file must not have been deleted from the disc. As you can see, you need to do a bit of server configuration to get it to work. When it does work, however, your users will greatly appreciate it. Let's see all of this in action. Open this episode's starter project, and start by creating a new model file called "MutableSongDownloader.Swift." Replace its import with OneSource SwiftUI. Next, add the following code for the class itself. This class is going to be similar in functionality to Song Downloader, but, you're putting this version in a different class in order for you to have a separation of how a URL session task works versus the URL session asynchronous transfer methods. In your apps and projects you can keep things separate or put them at all in a single networking class or layer. This code declares a published property that'll keep track of the downloaded song's location on disc. Add this code next. Where a song downloader creates a default URL session configuration and a URL session object with that configuration, this class creates a configuration that indicates Mutable Song Downloader is the session delegate. You don't do this in an Enit method in order to avoid having to force unwrap properties due to having to initialize them prior to the usage of self in Enit. An error will come up because Mutable Song Downloader doesn't conform to URL session delegate. So, add the following code to address that. The only required methods did finish downloading, too, but the other methods will help you get updates on the download as well as when an error occurs. Moving on, add two properties to your class. You'll use one to keep track of the download URL in order to resume the download and another one for the task that'll be used for background downloads. With your class set up for now, it's time to start working on the download logic. Add the following method. Similar to the methods you've written before, this method takes a URL for the location of the song to download. You store the URL from the parameter and you download URL property and create a new URL session download task from your URL session with the URL. Finally, and as was covered earlier in the course, you call resume on your task in order to actually begin the download. That's pretty much it for this method as far as downloading is concerned. Because you're implementing URL session delegate, you'll do more work there in a bit. To keep track of the download progress, add the following property. And then use it in the "did write data" URL session delegate method. This method is similar in concept to the one from URL session that returned an async sequence of bytes, except you get to download progress from the delegate method. You check the total versus downloaded bytes and update your download progress property. Note how you perform all of this in the main actor. So, this code runs on the main thread. Let's implement the delegate method for when a download error occurs. Here, all within the main actor, you check the response code and print an error if it's not 200. Once again, you're not focusing on handling all errors for now as you'll take care of that in a bit. Next, add the following code to implement the only required delegate method. Whew, that was a fair bit of code, but this is pretty much the same code from Song Downloader to handle what happens when your song is successfully downloaded. Once again, you print the error for now in order to focus on the important URL session concepts. Build your code to ensure everything is working well for now. Because Mutable Song Downloader can pause, resume, or cancel, you wanna wait to keep track of the state of your download. To do so, add the following Enum inside of your class. You declare states for paused, downloading, failed, finished, and waiting. Add a property for your state. Its initial value will be waiting as Mutable Song Downloader is waiting for any downloads to be performed. Let's keep track of your download state. Add this line at the bottom of the Download Song Method. This code sets this state to downloading when the download task starts. At this point, you've set up your class to download songs but using URL session task. The focus of this episode, however, is canceling, pausing, and resuming. Let's start with canceling. Add this method. Not a lot of code, so let's walk through it. You first set the state to waiting since the download is now canceled and never completed. You then call cancel on download task to actually cancel the download. Finally, and ensuring it's done on the main actor, you set your download progress to zero. So, any UI can be updated accordingly. For pausing a download, remember that you want to get a resume data object that you can use should you want to support resuming the download. So, add a property for your resume data. Then, write the pause method. You call cancel by producing resume data on your task. It requires that you implement a closure for when the data is ready. You store the return resume data in your resume data property and set the state of the download to pause. You also set the download progress to its existing value so that we may also trigger any additional UI updates that may be tracking the download progress property. With all of this done on the main thread, since this is a state property that might be tracked from the UI, the last method you need to write is the one for resuming, add the following code. The first thing it does is check whether you actually have any resume data before continuing. If you do, then you call download task with resume data on your URL session and store the task in your download task property. The task is started by calling resume and the state is set to downloading. Now, you need to handle the download state when the download finishes. Replace the call to print in the first guard statement with the following. This sets the state to failed if you are unable to acquire the path to the app's documents directory. On the line where you set the download location, add this afterwards. This will indicate the download finished successfully. Finally, replace the last print statement with the following. Should something go wrong when saving the song to disc, then you update the state to failed. Note how it updates to the state property we've done on the main actor. This is to ensure there are no bugs or problems from the UI side, since it's likely one to be tracked to match the UI to the state. The final thing to do is to replace the print statement in the delegate method for when a download error occurs. Excellent, you've wrapped up all work on mutable song downloading. There is more code and it's such cohesive given the delegate that is used but it's still clean, easy to read code that allows for pausing, resuming, and canceling your song downloads. With the work on the downloader wrapped up, switch over to SongDetailView.Swift. First, create a new property. Next, write a new method that uses the Mutable Downloader. This method leverages the downloader state property to know what to do when the button is tapped. If the song is downloading, then tapping the button pauses the download. If the download failed or the downloader is waiting, you ensure you have a valid URL and proceed to download the song. If the download finished, then you show the player sheet. And if the download is paused, you resume the download. This is the cool thing about using Enum for the state so you don't end up with a lot of "If" statements. Update the sheet modifier to use the Mutable Downloader's download URL. Now, update the "If" statement that determines whether to show the progress view or not. This checks the state of the downloader to determine the progress view's visibility. Finally, and in order to give everything a test, update the code for your button. The title of the button is now determined by the state of the Mutable Song Downloader. The action is always to call Mutable Download tapped, as it'll know what to do based on the Mutable Song Downloader's state. Build and run the app to test everything out. If the download is too fast, remember to use the network link conditioners so your download speeds are slower. Everything works as expected. Well done. Whew. That was a fair bit of work you had to do, but kudos for making it all the way to the end. You now have two versions of your song downloader. One that supports pausing, canceling, and resuming the download and uses URL session download task, and one that doesn't support these features but leverages the asynchronous transfer methods of URL session. And with that time for, of course, conclusion. I'll see you there.