Banelu
Banelu

Reputation: 313

swift invalidate timer doesn't work

I have this problem for a few days now and I don't get what I am doing wrong.

My application is basically just creating some timers. I need to stop them and create new ones. But at the moment stopping them doesn't work.

self.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target:self, selector: "timerDidEnd:", userInfo: "Notification fired", repeats: false)

That's my timer

func timerDidEnd(timer:NSTimer){
    createUnrepeatedAlarmWithUpdateInterval()
}

Because my timer didn't want to stop I am currently using the unrepeated timer and start it myself after it stopped.

func stopAlarm() {

    if self.timer != nil {
        self.timer!.invalidate()
    }
    self.timer = nil
    self.timer = NSTimer()
}

And that's how I stop my timer.

alarmManager.stopAlarm()
alarmManager.createUnrepeatedAlarmWithUpdateInterval()

I call the stopAlarm() function before creating a new timer.

I really don't know what I am doing wrong so I appreciate every answer :)

class AlarmManager: ViewController{

private var timer : NSTimer?
private var unrepeatedTimer : NSTimer?
private let notificationManager = NotificationManager()
private var current = NSThread()
private let settingsViewController = SettingsViewController()

func createRepeatedAlarmWithUpdateInterval(){

    var timeInterval:NSTimeInterval = settingsViewController.getUpdateIntervalSettings()

    if timer == nil{
    timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
        target: self,
        selector: "repeatedTimerDidEnd:",
        userInfo: "Notification fired",
        repeats: true)
    }
}
func repeatedTimerDidEnd(repeatedTimer:NSTimer){
    ConnectionManager.sharedInstance.loadTrainings(settingsViewController.getServerSettings())
    createUnrepeatedAlarm(10)
}

func createUnrepeatedAlarm(timeInterval:Double){

    unrepeatedTimer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
        target: self,
        selector: "unrepeatedTimerDidEnd:",
        userInfo: "Notification fired",
        repeats: false)
}
func unrepeatedTimerDidEnd(unrepeatedTimer:NSTimer){
    notificationManager.createNotification(self, reminderType: NotificationManager.ITEMRATINGREMINDER)
    notificationManager.createNotification(self, reminderType: NotificationManager.ITEMREMINDER)
    print("UnrepeatedAlarm ended")
}

func stopAlarm(){
    print("StopAlarm triggered")
    if (timer != nil)
    {
        print("stoptimer executed")
        timer!.invalidate()
        timer = nil
    }

    if (unrepeatedTimer != nil)
    {
        unrepeatedTimer!.invalidate()
        unrepeatedTimer = nil
    }
}
}

Thats the whole code of this class. Maybe that helps :D

Upvotes: 31

Views: 28654

Answers (5)

vadian
vadian

Reputation: 285069

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.

Upvotes: 55

Sergei Sevriugin
Sergei Sevriugin

Reputation: 498

For Swift 5 Xcode 12.4 there is example to use timer:

class MyController: UIViewController {

    private id: Float;

    func setValue(_ value: Float, withAnimation: Bool) {
            
            let step: Float = value / 200
            var current: Float  = withAnimation ? 0.0 : value
            
            let _ = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: withAnimation) { timer in
                
                DispatchQueue.main.async {
                    
                    self.id = current
                    
                    current += step
                    
                    if current > value || withAnimation == false {
                        self.id = current
                        
                        timer.invalidate()
                    }
                }
            }
        }
}

Upvotes: 0

Priyanka
Priyanka

Reputation: 169

I have tried every possible solution found but not able to resolve that at the end I have set repeat "false" while initialising timer like below

self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.methodname), userInfo: nil, repeats: false)

And need to add above line in my selector method for whatever the condition for which I wanted to repeat the time.

For example:- My requirement is I want to repeatedly call some method until one condition satisfied. So instead of adding repeats true I set it false as repeat true does not invalidate timer in my case.

I have added below in my viewdidload method

self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)

in selector function I added below code

@objc func method{

if condition not matched{
   self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
 }
 else{
    // once you are here your timer invalidate automatically
 }

}

Hope this will solve your problem

Upvotes: 0

Skwiggs
Skwiggs

Reputation: 1436

Something that's not really covered by the previous answers is that you should be careful your timer isn't scheduled multiple times.

If you schedule a timer multiple times without first invalidating it, it'll end up scheduled on multiple run loops, and invalidating it then becomes nigh impossible.

For me, it happened when calling my scheduleTimer() function in separate functions in my view controller's life cycle (viewWillAppear, viewDidAppear, ...)

So in short, if you aren't sure (or you cannot guarantee) your Timer is only scheduled once, just always invalidate it first.

Upvotes: 6

Kevin Kruusi
Kevin Kruusi

Reputation: 271

Make sure you're calling invalidate on the same thread as the timer.

From the documentation:

Special Considerations You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

https://developer.apple.com/documentation/foundation/nstimer/1415405-invalidate?language=objc

Upvotes: 21

Related Questions