9

I have lot of experience with other programming languages, but not so much in swift 3. I want to do polling loop. This is what i have written:

DispatchQueue.global(qos: .userInitiated).async {
            [unowned self] in
            while self.isRunning {
                WebService.getPeople(completion: nil)
                sleep(100)
            }
        }

This works fine for me, every 100 seconds, i do polling, and then make this thread sleep. What I am wondering, is this correct way to do this in swift 3?

Hamish
  • 78,605
  • 19
  • 187
  • 280
MegaManX
  • 8,766
  • 12
  • 51
  • 83
  • 1
    Firstly, don't if you can. But if you must, simply use a `Timer` – Paulw11 Jun 05 '17 at 11:44
  • 1
    As a general rule, don't block on a dispatch thread for long periods of time if you can avoid it. – JeremyP Jun 05 '17 at 14:44
  • @JeremyP i have heard others say the exact same thing, but i don't undestand why? If i dispatch on some low priority background thread, what trouble will that cause? – MegaManX Jun 06 '17 at 07:41
  • With GCD, tasks on queues are allocated threads from a pool which is calculated by the OS based on things like the number of CPUs. The thread can't be reallocated to another dispatch queue task while the original task is sleeping. – JeremyP Jun 06 '17 at 09:03

3 Answers3

15

You have 2 options:

  • Use NSTimer
  • Use a DispatchSourceTimer

Using NSTimer is pretty easy, but it needs an active run loop, so if you need to poll on a background thread things could be a little bit tricky, because you will need to create a thread and keep alive a run loop on it (probably the timer itself will keep the run loop alive).
DispatchSourceTimer on the other hand works using queues. You can easily create a dispatch source timer from one of the system provided queues or create one.

    var timer: DispatchSourceTimer?
    let queue = DispatchQueue.global(qos: .background)
    guard let timer = DispatchSource.makeTimerSource(queue: queue) else { return }
    timer.scheduleRepeating(deadline: .now(), interval: .seconds(100), leeway: .seconds(1))
    timer.setEventHandler(handler: { 
        // Your code
    })
    timer.resume()

The leeway arguments is the amount of time that the system can defer the timer.

Andrea
  • 26,120
  • 10
  • 85
  • 131
3

Swift 5, iOS 10.0+

The code in the accepted answer no longer compiles, it can be modified (and simplified!) into:

DispatchQueue.global(qos: .userInitiated).async {
    let timer = Timer.scheduledTimer(withTimeInterval: 100, repeats: true) { timer in
        // Your action
    }
    timer.fire()
}
Higgs
  • 550
  • 5
  • 18
3

Swift 4 & Swift 5

 var timer: Timer? //declare outside function scope
 var runCount = 0


 self.timer = Timer(timeInterval: 2.0, target: self, selector: #selector(self.fireTimer), userInfo: nil, repeats: true)

 guard let timer = self.timer else {return}
 RunLoop.main.add(self.timer, forMode: RunLoop.Mode.default)

 @objc func fireTimer() {
         print("Timer fired! \(runCount)")
        runCount += 1

        if runCount == 5 {
            timer?.invalidate() //stop the timer
        }
    }

OR

self.timer = Timer.init(timeInterval: 1.0, repeats: true, block: { (timer) in
        print("\n--------------------TIMER FIRED--------------\n")
        runCount += 1
        if runCount == 5{
          timer.invalidate()
    }
    })
guard let timer = self.timer else {return}

RunLoop.main.add(self.timer!, forMode: RunLoopMode.defaultRunLoopMode)
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78