Why Would a 'Scheduledtimer' Fire Properly When Setup Outside a Block, But Not Within a Block

Why would a `scheduledTimer` fire properly when setup outside a block, but not within a block?

The issue is that the completion block in question was probably not running on the main thread and therefore didn't have a run loop. But timers need to be scheduled on a run loop, and while the main thread has one, most background threads do not (unless you add one, yourself).

To fix this, in that completion handler, dispatch the creation of the timer back to the main thread and it should work fine:

DispatchQueue.main.async {
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: true)
}

Or use a dispatch source timer (a timer that can be scheduled for a background queue, and doesn't require a run loop).

var timer: DispatchSourceTimer!

private func startTimer() {
let queue = DispatchQueue(label: "com.domain.app.timer")
timer = DispatchSource.makeTimerSource(queue: queue)
timer.setEventHandler { [weak self] in
// do something
}
timer.schedule(deadline: .now(), repeating: 1.0)
timer.resume()
}

For syntax for earlier version of Swift, see previous revision of this answer.

Timer.scheduledTimer not firing

Your code works for me, but that isn't how you want to time an event because the Timer isn't that accurate, and you are wasting a lot of computing time (read battery).

Instead, I suggest the following approach:

When the event starts, record the start time:

let startTime = Date()

When the event ends, compute the elapsed time:

let elapsedTime = Date().timeIntervalSince(startTime)

elapsedTime will be in seconds (including fractional seconds).

Swift 3 Timer in a Class not firing

Change

msgTimer = Timer(timeInterval: 0.5, target: self, selector: #selector(typeMessage(_:)), userInfo: nil, repeats: true)

to

msgTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(typeMessage(_:)), userInfo: nil, repeats: true)

dismissViewControllerAnimated does not work within a block

In case someone is having the same issue. I pushed the UIViewController, I didn't present it with presentViewController:animated:completion:. That's why [self.navigationController popViewControllerAnimated:YES]; should be used instead.

The strange thing is that [self dismissViewControllerAnimated:YES completion:nil]; worked outside of the block and didn't inside, I have no explanation for this...

Use selector to call function with parameters

EDIT: Updating to include pre-iOS 10.0 solution

In this case it'd be better to use the scheduledTimer function that takes a block, as this will allow you to pass arguments to your function:

Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { (timer) in
SystemClass().setXYZ(xyz: 1.0)
}

For pre-iOS 10.0:

You can also pass userInfo in, including this default value, and then access that info from your inside your selector, like this:

Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(SystemClass().setXYZ(_:)), userInfo: 1.0, repeats: true)

To do this you'd also have to modify your setXYZ function signature to look like this:

@objc func setXYZ(_ sender: Timer) {
if let xyz = sender.userInfo as? Float {
print(xyz)
// do something
}
}

swift invalidate timer doesn't work

The usual way to start and stop a timer safely is

var timer : Timer?

func startTimer()
{
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
}

func stopTimer()
{
timer?.invalidate()
timer = nil
}

startTimer() starts the timer only if it's nil and stopTimer() stops it only if it's not nil.

You have only to take care of stopping the timer before creating/starting a new one.



Related Topics



Leave a reply



Submit