Modern Concurrency: Getting Started

Oct 18 2022 · Swift 5.5, iOS 15, Xcode 13.4

Part 2: Asynchronous Sequences

13. Downloading Chunks

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: 12. Displaying a Progress View Next episode: 14. Canceling Tasks

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.

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

Make sure the course server is running and continue with your project from the previous episode or open the starter project for this episode. Build and run the project. Select the file and tap silver. You've coded the silver download option, which fetches the complete file in one go and presents an onscreen preview. In this episode, you'll implement the gold download option, which provides progressive UI updates as you download the file in chunks. You'll do this by reading the file as an asynchronous sequence of bytes from the server. This is similar to what you did in episodes four and five, but now you'll implement some helper methods to work with chunks or batches of bytes. This lets you update the progress bar one chunk at a time as you're receiving the files' contents. In SuperStorageModel, locate downloadWithProgressFile. This public method calls the private version of downloadWithProgress defined just below. This private method is where you'll add more code. Currently it defines the endpoint and adds the file name to the published downloads array. Then it defines a tuple to hold the result of an asynchronous URL session method call. Option + click AsyncBytes. This method returns a URLSession AsyncBytes sequence, which gives you the bytes it receives from the URL request asynchronously. This closure is empty for now. You don't need it for this episode. Offset is an optional int parameter with default value nil. The silver download method doesn't set offset, and neither will the gold method. Only the Cloud 9 plan uses the code in this closure. You'll add this code in episode 16 when you implement the Cloud 9 plan. For silver and gold download plans, you just need this standard URLSession code similar to what you've already written. You use URL to make a standard request and check for the usual success status code. Option + click result. A successful response gets you an asynchronous byte sequence stored in result.downloadStream. Now above the dummy return statement, create an asynchronous iterator for the sequence. This asynchronous iterator gives you more control over the incoming bytes to update the progress bar. Now Xcode flags an error because the if closure doesn't initialize result. Just comment out the if else lines for now. You won't update the progress bar after every byte. Instead, you'll process a batch of bytes at a time and update the progress bar after each batch. The starter project includes a custom byte accumulator class. In the project navigator, in the Model group, open ByteAccumulator.swift. Each chunk or batch of bytes is 20 bytes. Click back to SuperStorageModel and downloadWithProgress to create a byte accumulator and prepare to use it. StopDownloads is a flag you'll set in the next episode. So while the accumulator can still collect more bytes, these two conditions give you the flexibility to run the loop until either the external flag stopDownloads is set or the accumulator completes the download. With this design, you're looking ahead to make it easy to cancel the download code by using an external flag. Now add more code inside the while closure. So you put a nested while that runs until this batch is full. The nested while runs until the batch is full or the byte sequence completes. You have to await each byte downloaded and processed by your asynchronous iterator. After a batch completes, it's time to update the download progress bar. Add this line in the outer while closure but after the inner while loop. Jump down to updateDownload to see where this progress value gets used. It updates the current download's progress value. Right-click this progress property. Find its call hierarchy. Select Downloads.getter.body. This downloads view passes it to progress view. Get back to SuperStorageModel updateDownload. Remember, updateDownload belongs to main actor, so it runs on the main thread, not on whatever threads the iterator and accumulator are running on. This is a safe place to try out task.detached. Apple documentation recommends against using task.detached because it negatively affects the concurrency model's efficiency. But just to see how it works, you'll create the task so it doesn't slow down the ongoing download task. Get back to the last line you added in the downloadWithProgress while loop and wrap it in a detached task. Now that updateDownload is inside a closure, you need a self dot. DownloadWithProgress is a task started by a user action, so it runs with user-initiated priority. This detached task doesn't inherit its parents' task storage, execution actor, or priority. Explicitly setting its priority to medium means there's no chance it will slow down the download task. But now that this is a separate task, using accumulator.progress here could cause a data race. So before creating the detached task, capture accumulator.progress in a local constant. Then pass this value to updateDownload. Now add a print statement to keep track of downloads during development. And finally replace the dummy return value. While processing the file in batches of 20 bytes, the accumulator has saved every byte to its data property. When all the bytes have downloaded, you have a file. And now to call your new method, go to DownloadView and fill in the downloadWithUpdates action closure. First copy and paste the downloadSingleAction code and change downloadFile to downloadWithProgressFile. Build and run. Select a file, then tap Gold. Now the Progress bar updates a little at a time and you see the progress printed in the console. We've implemented the progress bar feature of the gold plan. In the next episode you'll add more features while learning how to cancel tasks.