Chapter 4 - Babble Timer countdown(to:) in Xcode 14 (Swift 5.7)

The countdown timer never fires.

Adding @MainActor to the function fixes the problem.

Full code:

  func countdown(to message: String) async throws {
    guard !message.isEmpty else { return }
    var timer: Timer? // Do we need a reference to fire, surely not (test code)

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

        // FAILURE ETC ...

    for await countdownMessage in counter {
      try await say(countdownMessage)
1 Like

@icanzilb code errata?

Hey @robertomachorro - Xcode 14 is still in beta, we usually don’t support pre-release versions

@peter.suwara as you can see from the author’s response above, beta versions of Xcode/Swift are understandably not supported. Beta versions may contain changes and incompatibilities, they may also change over time and create a moving target for updates. Xcode can run both release and beta versions in the system, please use the current release.

Ok, thanks for the update guys.

1 Like

Thanks Robert. Also, how do I add an avatar picture to my profile?

I believe that this is picked up from your Gravatar:

Thanks @robertomachorro, I’ve updated my Gravatar :smiley:

1 Like

Seems like things will break in Swift 6. We shouldn’t be holding a reference to async code like this. I take it you know about this issue @icanzilb ? :


Sure Peter, I do know about this. RayWenderlich books receive updates, those are usually on a yearly basis — each edition targets a given Xcode/Swift version. If you open the book you will see it targets Xcode 13 and Swift 5.5 because these were the latest versions at the time this edition was produced. Any future editions will support the latest versions at the time those are made. Obviously since these new warnings were introduced, future editions will feature different code in the chapter you are referring to. I hope that helps

1 Like

Xcode 14 is not in Beta anymore. And if warnings are not a big issue, missed @MainActor from initial post is an issue which breaks app, because no runloop → no timer. No timer → No messages

Hi @kolya-j ! Welcome to the Forum. Xcode was recently released and we are all exited about it. Having the expectation of books immediately after is tough, all books are updated continuously, but again, not immediate.

Hi @kolya-j as Roberto noted if the book says the content targets Xcode 13 that’s the version that is supported. A later edition will be specifically suited for Xcode 14.

That said, I’m using Xcode 14.1 here and I don’t see an issue with Chapter 4. We’ve done a round of updates to address readers feedback lately, can you have a look if the issue you have is resolved? If not, could you post a more detailed description so we can assess if that’s something we should address adhoc before the next edition?

Thank you

I think I miss fact that book asks to use xcode 13.0. But now… thinking about code a bit more… It doesn’t matter which xcode you are using. Logical issue there not connected to xcode or iOS version.

What I did:

  1. Got to GitHub - raywenderlich/mcon-materials: The projects and materials that accompany the Modern Concurrency in Swift book
  2. See only one branch - editions 1.0, so I download code from there.
  3. Open 04-async-streams/projects/final/Blabber.xcodeproj
  4. Run server and run app
  5. In chat use timer button to trigger countdown.

What should happen:
We should start timer and timer called every second

What happen:
Timer never triggered, therefore no messages send to server

Why it happens this way?

  1. Button(action: ) takes sync block. When you start task inside sync code - no context exists, therefore Task starts on random queue… most importantly non-main. Even if initial clouser was called on main thread. I saw this issue on xcode 13 as well, one of first things I learned hard way.
  2. If you call schedule timer from non-main thread - it is most likely won’t work, because timer works only inside of runloop. And no one spin runloop in any romdom queue
  3. I attached screenshot confirmign my point about non-mina queue call, you can see it in debugger

I honeslty beleive issue was there on xcode 13, 14 and will be on 14.1. At least this is my currenctt understanding of asyc code, I’m still reading the book, but already have some experience with it.

And fix is really simple - you just need require main thread execution of countdown fucntion, by adding @MainActor.

Screenshot 2022-10-15 at 18.21.23

@kolya-j I see, that was helpful — I didn’t realize we’re talking about the final project. Now I can reproduce the issue and we’ll fix it. This must’ve been introduced in a recent Swift version — I don’t believe it’s possible that all of the editors and 1000s of readers didn’t notice this before.

I downloded xcode 13 and I can confitm, that it indeed works in last xcode 13. Which is suprise for me.
About fix, it also should be done in starter project because, countdown function exists in it already.

Thank you for your help!