Transcript: 03. Using AsyncStream to Count Down
For the rest of this part of the course, you'll work on this messaging app. In the next two episodes, you'll add a countdown timer, show notifications of users arriving and leaving, and in episode 7 and 8, you'll implement this location button. I click the wrong button there. Most of the projects in this course interact with a server. It's included in the Course Materials. In Finder, open the Course Materials folder and find the Course Server folder. Course Server is a vapor server that you run in a terminal. So open terminal next to your Finder window. First you need to change directory to Course Server. In terminal type CD space, then drag the Course Server folder from Finder into terminal and press Return. Type ls to list the files in Course Server. This Package.swift file is what you'll run to start the server. Type Swift Run to run the Swift package. When you see the server starting message, check the connection by opening a browser window and loading local host 8080/hello. You can stop the server by pressing control C and restart it with Swift Run. Press the up arrow to get Swift Run back, then press Return. Restarting is quick because you already downloaded everything. If you leave this server running, it might stop serving data correctly. If the app doesn't work as you expect, stop and restart the Course Server. In the Course Materials, locate the Blabber Starter project and open it. In the Project Navigator, open the Views and Model groups. Starter projects in this course have all the views navigation and data model already set up so you can concentrate on writing the concurrency code to make the app work. The views you'll be working on are outside the views group for quick access, build and run the app. You can log in to see the chat screen with the status message, one active users and maybe a random message from Botley, the chat bot. You can send messages, but it's not much fun chatting with only Botley. Build and run on another simulator. Log in as your favorite author and make up a conversation. Here's a quick walkthrough of the code that's implementing this basic chat app. In Blabber model, the main actor chat method already has the usual URL session code. Like the Littlejohn stock ticker app in the preceding course, Blabber uses the live URL session custom session. It makes requests that never expire or timeout so the app can keep receiving from the server indefinitely. Scroll down a little to see some "try await" code. Once it establishes a connection, chat calls read messages as the operation of the with Task Cancellation Handler method. This executes its operation and immediately invokes the handler if the task is canceled. Scroll down a little to read messages. Read messages looks a lot like what you did in Littlejohn, fetching and showing stock prices, but there's one difference. The asynchronous sequence lines are not all the same. You have to process the first line separate from the user's chat messages. When your app opens a connection to the server, the first line it receives is a status message like "active users for". The chat messages follow on separate text lines. So you handle the status message first with this iterator. You use an iterator when you know exactly how many lines you want to handle. In this case, just one. Iterator.next is the first line, the status message. The text line is converted to data and then decoded to a server status message. Jump to the definition of server status. It's a data model containing the single property active users. This keeps track of how many users are in the chat at the moment. There is also a message data model. It has a convenience initializer that only needs a message value. Click Back to Blabber model. Scroll up to see this. There's this published array of messages. Now jump back to read messages. And this is where the first message gets stored. This first status line in the server response uses the message convenience initializer to create a system message. After this, there's an open-ended sequence of user messages processed in a for loop, just like the stock prices in Littlejohn. If a line decodes as a message, it's added to the messages array. So the starter app has the basic chat facility and it's time to add your first feature. In the simulator, the chat view has two buttons next to the message field. You'll implement the show location button in episode 7 and you'll implement this countdown button now. In a chat app, a countdown feature adds an element of drama by counting down before showing the latest message. It will start at three countdown to one then terminate with the user's message. You'll implement this with a pull based AsyncStream. Open Chat View. Scroll down to the second button in the HStack. This button's action calls the models "countdown to" method. Jump to "countdown to" in Blabber model. Add some code. You initialize your counter variable countdown to three. Then create a pull based AsyncStream that produces string values. In its asynchronous closure, you need to return the next sequence value. First, wait one second. Task.sleep throws a cancellation error if the current task is canceled while it's sleeping. Throwing an error is the quickest way to cleanly and safely exit the current execution without waiting the specified amount of time. After the one second wait, return a message depending on the countdown value. If countdown is three, two, or one, return that value. If countdown is zero, return the user's message. When countdown becomes minus one, return nil to signal the end of the sequence. Now, just before the switch, decrement countdown. All the switch cases exit the closure, so you use a defer statement to squeeze in the decrement. Add this code at the bottom of the method outside the counter closure. This 408 loop asks for the next element in the counter AsyncSequence until it gets nil. Build and run the app. Log in. Type a message. Then tap the timer button. You get a three, two, one countdown and then your message appears. Well done. You created a timer with an unfolding pull based AsyncStream. In the next episode, you'll create a buffered AsyncStream to monitor notifications.