Modern Concurrency: Beyond the Basics

Oct 20 2022 Swift 5.5, iOS 15, Xcode 13.4

Part 1: AsyncStream & Continuations

4. Using AsyncStream for Notifications

Episode complete

Play next episode

Next
Save for later
About this episode
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 3. Using AsyncStream to Count Down Next episode: 5. Using a Buffered AsyncStream

This video uses Xcode 14’s Task.sleep(until:clock:). If you use Xcode 13, replace this with Task.sleep(nanoseconds: 1_000_000_000).

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

You can unlock the rest of this video course, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.

Refresh your browser to make sure the course server is running or restart the server in Terminal. Continue with your project from the previous episode or open the starter project for this episode.

Adding an asynchronous stream to NotificationCenter

Chat users expect to see system messages when another user leaves the chat or returns. Your app can get this information from NotificationCenter, one of Apple’s built-in frameworks.

func notifications(for name: Notification.Name) -> AsyncStream<Notification> {
  AsyncStream<Notification> { continuation in

  }
}
NotificationCenter.default.addObserver(
  forName: name, 
  object: nil, 
  queue: nil
) { notification in
  continuation.yield(notification)
}
func observeAppStatus() async {

}
for await _ in await NotificationCenter.default
  .notifications(for: UIApplication.willResignActiveNotification) {
  
}

Notifying participants when a user leaves

Now, add code in the loop to post a system message when a user leaves the chat:

try? await say("\(username) went away", isSystemMessage: true)
let notifications = Task {
  await observeAppStatus()
}
defer {
  notifications.cancel()
}

Notifying participants when a user returns

In BlabberModel, jump to observeAppStatus(). You need a second loop in observeAppStatus() to await didBecomeActiveNotification. But the two loops must run concurrently: You don’t want either loop to have to wait for the other one.

🟩Task {🟥
  for await _ in await NotificationCenter.default
        .notifications(for: UIApplication.willResignActiveNotification) {
    try? await say("\(username) went away", isSystemMessage: true)
  }
🟩}
Task {
  for await _ in await NotificationCenter.default
        .notifications(for: UIApplication.🟩didBecomeActiveNotification🟥) {
    try? await say("\(username) 🟩came back🟥", isSystemMessage: true)
  }
}