Modern Concurrency: Beyond the Basics

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

Part 1: AsyncStream & Continuations

05. Using a Buffered AsyncStream

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: 04. Using AsyncStream for Notifications Next episode: 06. Unit Testing

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.

Notes: 05. Using a Buffered AsyncStream

Use Xcode 13 for this exercise. Xcode 14 beta uses Swift 6, which doesn’t allow using countdown in the scheduledTRimer closure.

Transcript: 05. Using a Buffered AsyncStream

Your challenge is to rewrite countdown(to:) using the buffered push-based version of AsyncStream.

Use Timer instead of Task.sleep to wait 1 second between messages. The starter project already has code for a Timer.scheduledTimer that fires every second.

Inside the scheduledTimer closure, yield the correct message when countdown is 3, 2, 1 and 0. Remember to decrement countdown after yielding 3, 2 and 1.

When countdown is 0, return the user’s message, invalidate the timer and finish the continuation.

Welcome back! Hopefully you had success with this task. Here’s how I did it.

In BlabberModel, countdown(to:) already has starter code to create a Timer that fires each second, instead of the pull-based AsyncStream code you wrote in episode 3:

let counter = AsyncStream<String> { continuation in
  var countdown = 3
  Timer.scheduledTimer(
    withTimeInterval: 1.0,
    repeats: true
  ) { timer in

  }
}

Inside the timer closure, I first handled the countdown digits:

continuation.yield("\(countdown) ...")
countdown -= 1

On every timer tick, I called yield(_:) on the continuation to produce the countdown value, then decreased countdown.

Next, I needed to add code for when countdown reaches 0.

I did this before continuation.yield...

) { timer in
  🟩guard countdown > 0 else {
    timer.invalidate()
    continuation.yield("🎉 " + message)
    continuation.finish()
    return
  }
  🟥continuation.yield("\(countdown) ...")
  countdown -= 1
}

When countdown reaches 0: I stop the timer, yield the user’s message, and call continuation.finish() to complete the sequence.

Actually, there’s a shortcut to produce the last value and complete the sequence at the same time:

continuation.yield(with: .success("🎉 " + message))

To show it works:

The next episode is about unit testing.