-3

I have a simple timer each 1 second will execute logging print inside an async Grand Central Dispatch:

import Foundation

do {
    let GDC = DispatchQueue.init(label: "GDC", attributes: .concurrent)
    
    GDC.async {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            print(3333)
        }
    }
 } catch let err {
     print(err)
 }

However I don't see the log of print or err when running in Playground?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Kim Mỹ
  • 386
  • 1
  • 4
  • 16
  • As an aside, you mention Playgrounds. On top of the problem regarding scheduling a runloop-based `Timer` on a background queue, make sure that you `import PlaygroundSupport` and `PlaygroundPage.current.needsIndefiniteExecution = true`. (You may be doing that already, but just a FYI for future readers.) – Rob Mar 31 '23 at 18:19

1 Answers1

-1

Technically to solve the issue you need to create a permanent loop using RunLoop.current.run()

So:

do {
    let GDC = DispatchQueue.init(label: "GDC", attributes: .concurrent)
    
    GDC.async {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            print(3333)
        }
        RunLoop.current.run()
    }
 }

should work. However do you really need timers in this case is a bigger question. Consider using DispatchQueue.asyncAfter for example (and possibly there are more options, depending on use case).

timbre timbre
  • 12,648
  • 10
  • 46
  • 77
  • The ol’ “spin up another run loop” pattern which we would use in the pre-GCD days is an anachronism. For light tasks, it is better to just schedule it on the existing run loop (i.e., the main run loop) rather than spinning up another. And if you really want to run it on a particular GCD queue, you’d just use a GCD timer, again avoiding a new run loop. And, if all that, you really want to spin up a new run loop, don’t use `run`. As its [documentation](https://developer.apple.com/documentation/foundation/runloop/1412430-run) says, use `run(mode:before:)`. – Rob Mar 31 '23 at 15:51
  • @klangfarb I want to check a condition that appears in the future. Does this solution has any tradeoffs? – Kim Mỹ Mar 31 '23 at 16:25
  • 1
    @Rob in my many years with iOS dev, I would never use timer in this manner all together, and I could count on one hand the number of times I had to affect RunLoop directly. However the question was abstract and about playground, so I answered in the simplest way possible. – timbre timbre Mar 31 '23 at 16:27
  • @KimMỹ For this particular approach - yes, there's a lot to improve. See Rob's note on run loop handling (and in general: if you need to affect RunLoop directly, you are already in workaround mode). So if it's a one time check, then definitely `DispatchQueue.asyncAfter` would be better. If it's a periodic network task of some sort, then I suggest using `Combine` (which also has timers, but not only, it has many advantages over this approach). – timbre timbre Mar 31 '23 at 16:34
  • 1
    @KimMỹ - “I want to check a condition that appears in the future.” … A repeating timer is an inefficient way to do this. (Admittedly, sometimes we have to do this, but that is rare; we only do this when absolutely necessary.) We generally try to avoid “polling” to see when some condition occurs. We would generally want that “condition” to notify us when the event takes place (with some observer pattern, notification pattern, etc.). If you give us a better sense of what sort of condition you are testing, we could probably offer better counsel. Perhaps post a separate question with more details. – Rob Mar 31 '23 at 18:16