so I'm reading the Modern Concurrency book from raywenderlich.com and I assume the book must be outdated or something, I'm trying to run the closure
insde the AsyncStream
but it doesn't seem to get there, I'm still pretty new to this Async/Await thing, but when adding some breakpoints I can see my code is not getting there. This is my code and a screenshot with some warnings showing. I am not really familiar with what the warnings mean, just trying to learn all this new stuff, I would truly appreciate some help and is there a way to fix it with Swift 6? Thanks in advance!
Reference to captured var 'countdown' in concurrently-executing code; this is an error in Swift 6
Mutation of captured var 'countdown' in concurrently-executing code; this is an error in Swift 6
func countdown(to message: String) async throws {
guard !message.isEmpty else { return }
var countdown = 3
let counter = AsyncStream<String> { continuation in
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
continuation.yield("\(countdown)...")
countdown -= 1
}
}
for await countDownMessage in counter {
try await say(countDownMessage)
}
}
Timer.scheduleTimer
requires that it be scheduled on a run loop. In practical terms, that means we would want to schedule it on the main thread’s run loop. So, you either callscheduleTimer
from the main thread, or create aTimer
and manuallyadd(_:forMode:)
it toRunLoop.main
. See the Scheduling Timers in Run Loops section of theTimer
documentation.The easiest way would be to just isolate this function to the main actor. E.g.,
There a few other issues here, too:
I would suggest defining the
countdown
variable within theAsyncStream
:The
AsyncStream
is never finished. You might want to finish it when it hits zero:There should be a
continuation.onTermination
closure to handle cancelation of the asynchronous sequence.Going back to the original question (why this is not running), I personally would avoid the use of
Timer
in conjunction with Swift concurrency at all. A GCD timer would be better, as it doesn’t require aRunLoop
. Even better, I would adviseTask.sleep
. Needless to say, that is designed to work with Swift concurrency, and also is cancelable.I personally would suggest something like: