Modern Concurrency: Getting Started

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

Part 1: Asynchronous Code

04. Asynchronous Sequences

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: 03. Your First Asynchronous App Next episode: 05. Using AsyncSequence in Views

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.

In this episode, you'll learn more about async/await, then start exploring asynchronous sequences. You'll need this to implement the live ticker view in the Little John app. In the course materials, locate the starter playground and open it. First, here are two more ways to use async/await. Here are the data model and fetchDomains function from episode two. And the task that calls fetchDomains. You can mark read only computed properties with async and throws. Find the to do for extendDomainsWithGetter and add this extension. You've moved the call to fetchDomains into the computed properties getter. Now, in task, comment out let domains equals try await fetchDomains and edit the for in line. You're using the computed property. Because it's asynchronous and can throw errors, you use try await. Run the task. And you get the same results as before. Now scroll up to the second extension. You can use your async computed property domains to create an asynchronous read-only subscript. Replace this dummy return statement. You use domains to locate the requested element and throw an error if the requested index is out of range. Now create a task above the existing task to use this subscript. Option click on dump to show its documentation. You're showing the contents of the fifth element. You must try await because subscript uses domains, which is an async throwing computed property. Run this task. It's Game Tech. You might get a different domain. Sometimes new ones appear or old ones get renamed. Click to the next page. Another powerful abstraction that Swift Concurrency gives you is the asynchronous sequence. It's like the standard Swift sequence, except getting each element may cause the task to suspend. Here's an async function that iterates over an asynchronous sequence. You can iterate over an aync sequence with a for in loop just like a non-async sequence, but you need the try await keywords. The URL type has a lines property that returns an asynchronous sequence of strings, one string for each line in the file. Here you're expecting to receive an HTML file for a webpage, and you read each line until you find its title tag. You loop over this sequence with for try await. It can stop loading and return the answer as soon as it hits a line with the title tag in it. Now create a task to test this function. You're reading through the raywenderlich.com homepage, looking for the line with the title tag. Run this task. And here's the title. The for in loop is really a while loop calling the next method of an iterator for the sequence url.lines. You can create the iterator yourself if you only want a fixed number of elements. Add these lines to the task. You create an iterator and print the next line, if there is one. Run the task. And here's the title and the next line. URL and some other built-in types have built-in async sequences, but how do you create your own async sequence? Take a closer look at async sequence. Option click lines, then click async line sequence to open its documentation. Click async sequence. The async sequence protocol lets you create your own async sequences. Timer, notification center, uidevice.orientation are a few examples. An asynchronous sequence lets you access its elements via its iterator. Scroll down to topics. The async sequence protocol only requires you to define the element type of the sequence and provide an iterator. As you just saw with the lines example, the iterator also powers for await in loops. The sequences iterator must conform to async iterator protocol, which is also very minimal. Click it to see. Scroll down to topics. The only requirements are the element type and an async method that returns the next element in the sequence. To see how this works, you'll create a simple typewriter, an asynchronous sequence that types a phrase, adding a character every second. Close the documentation window. Scroll up to func findTitle, and above it, add a new structure. Wait for Xcode to offer to add protocol stubs and click fix. Set the async iterator type alias to typewriter iterator. And the element type alias to string. Below this structure, add a typewriter iterator structure. Xcode knows you need a next method, and because your element type is string, the next method knows it has to return an optional string. For now, just return an empty string. Now that you have a placeholder iterator, let Xcode add a stub to typewriter so it conforms to async sequence. Click fix, and for now, just return a default typewriter iterator. I like to have the type alias lines first, so I'll move them up. You've now got the bare bones of a custom async sequence. It needs a phrase property. Add the property, then pass it to typewriter iterator. And also add this property to your iterator. And start using it. To step through the characters in phrase, you need to keep track of its index, so you initialize index to the starter phrase. Now replace the placeholder return in the next method. First, check you haven't reached the end of phrase. Then, introduce a one second delay before you return the substring. Just before this method returns, increment index. And finally, return the substring of phrase between its start index and the current index. So each time you call next, it returns a substring of the initial string that is one character longer than the last one. When it reaches the end of the phrase, either by a for await loop or some code that calls next directly, next returns nil to signify the end of the sequence. Now add a task to try out your typewriter sequence. This is a sequence, so you can iterate over it. The for in loop uses this next method of typewriter iterator. Run this task. So it's pretty easy to create a custom async sequence. You just have to add two extra types to your code base. To avoid clutter, you can make a single type conform to both async sequence and async iterator protocol, but there's also another much easier way. You'll learn about async stream in the next course, Beyond the Basics. Now click to the next page. To finish this episode, learn how to cancel a task, and here's why the second task has a name. You need its name to cancel it. Scroll down to the end of the playground and add these lines. And run. Well, that didn't change anything. The named task still computed some. Task cancellation is cooperative. The cancel method only sets the is canceled flag of the task. You need to to check the task's cancellation status before it does any expensive work. Add a line before the sum calculation. If you call cancel before task begins to run, check cancellation throws a cancellation error. Run this code. The unnamed task delays the start of task, so task.cancel happens before task starts, and so task exits before it calculates sum. If you want more control over what happens when a task is canceled, use task.isCanceled. Comment out try Task.checkCancellation. And then add this code. You print a message and then manually throw cancellation error. Run this code. There's your message, and the named task exits without calculating sum. In the next episode, you'll see another way to check for cancellation. The URL session API throws custom errors and has a dedicated cancellation error code.