Previous episode: 10. Concurrency With async let
Next episode: 12. Displaying a Progress View
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.
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, then tap a file name. The DownloadView downloads the file using your choice of subscription plan, Silver, Gold, or Cloud 9. In this episode, you'll implement the slowest plan, Silver. For this plan, the server can pretend to be slow. There's nothing wrong with your network connection. In SuperStorageModel locate download file. It already contains the code you know so well. This code creates an endpoint, sends a request to the server, waits for the response, and then checks the response status code. All you need to add is some housekeeping to keep track of downloaded files. Look down in the model's extension to see two helper methods, addDownload and updateDownload. Go back to download file and add this line before sending the URL request. You add the file's download info to the published downloads array. The DownloadView uses this to show ongoing download statuses on screen. Then, add this line after sending the URL request before checking statusCode. After data from completes, you set progress to 1.0 to indicate the download finished. In later episodes, you'll implement the Gold and Cloud 9 plans, where you'll update the progress value during the download. To finish download file, replace its return value. Now, where do you call this asynchronous method? Open DownloadView. Go to its body. This view's body is a list, and the main list item is a FileDetails view. This view's initializer has three action parameters, one for each of the subscription plans. Right-click FileDetails and jump to its definition. Here's the Silver button with action downloadSingleAction. Click back to DownloadView. You need to make downloadSingleAction call download file. Try this, even though you feel in your bones it won't work. Sure enough, the parameter expects a synchronous closure. You change FileDetails to make it accept asynchronous closures. Click back to FileDetails and add the async keyword. But the button action must be synchronous. Also, you don't always have access to the source code of an API. Remove the async keyword and go back to DownloadView. You can't use .task view modifier. That would execute this code when the view appears. To pass an asynchronous closure to a button action, you need something that lets you run asynchronous code in a synchronous context. You did this right at the start in episode two. That's right, you wrap the asynchronous code in a capital T Task. Build and run, select a JPEG file 'cause they're smaller, and then tap Silver. A progress bar appears, and, after a while, it updates and a preview appears. But here in Xcode, there's a console message that publishing changes from background threads is not allowed. You might also get a purple flag from the main thread checker. Why is this happening? You passed download file to FileDetails, which is a SwiftUI view, so, when you tap Silver, download file starts running on the main thread. Jump to the definition of download file to take another look. Here's the problem. This await suspends your code. When it resumes and calls updateDownload, it could be executing on any thread, probably not the main thread. Jump down to updateDownload. This method updates downloads. Jump up to downloads. It's a published property, so you need to make sure updateDownload runs on the main thread. And look at addDownload. It also updates downloads, so it also must run on the main thread. Look again at download file. Because you called addDownload before the await, it's already running on the main thread. But that's really a coincidence, and you can't count on this always happening. You'll call it for the other download methods, and there's no guarantee it will always run before code suspends, so you need to do something for both addDownload and updateDownload. In episode five, when you assigned a new value to ticker symbols, you enclosed that line of code in a MainActor.run closure. This time just tell Xcode that these two methods belong to MainActor. Annotate them with @MainActor. Now, both methods will always run on the main thread. By default, code that belongs to an actor runs asynchronously, so you need to call it with await. This lets the runtime suspend your call and resume it on the correct actor. Scroll back to download file, and there are two new error messages. The fix insert await is correct, so go ahead and click Fix. Build and run again. Pick a file and tap Silver. This time, there's no error message in the console. Tap Back and pick another file. Hmm, no buttons, just the progress bar from your first download. This one's a quick fix. In DownloadView, add this modifier below toolbar. You reset the view's fileData and also the model's downloads array. Build and run again to see that this works. Yep, that's fine. You're almost finished with the Silver plan. There's just one more feature to add. In the next episode, your challenge is to show a progress view spinner while the file is downloading.
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.