50

We call startTimer function to start a timer. When we wanted to stop it we call stopTimerTest function but after we called stopTimer function the timerTestAction keeps firing. To check the timer condition we used print and print in timerActionTest returns nil.

var timerTest: Timer? = nil

func startTimer () {
    timerTest =  Timer.scheduledTimer(
        timeInterval: TimeInterval(0.3),
        target      : self,
        selector    : #selector(ViewController.timerActionTest),
        userInfo    : nil,
        repeats     : true)
}

func timerActionTest() {
    print(" timer condition \(timerTest)")
}

func stopTimerTest() {
    timerTest.invalidate()
    timerTest = nil
}
Igor
  • 12,165
  • 4
  • 57
  • 73
Hope
  • 2,096
  • 3
  • 23
  • 40
  • 1
    try to declare `var timerTest : Timer?` then in `startTimer` before instantiating add `if timerTest == nil { your instantiation}` and in `stopTimerTest` add `if timerTest != nil { your code}` – Mat Oct 17 '16 at 08:27
  • 1
    **if timerTest == nil { your instantiation}** is made the day brighter. – Hope Oct 17 '16 at 08:47
  • can you post some code which compiles has suggested by Vadian? – Mat Oct 17 '16 at 09:18
  • 1
    Mr. @mat your comment is the answer my question. If you can make it an answer I can check it as answer. If you want of course, no pressure. – Hope Oct 20 '16 at 07:13
  • @Hope I am glad it fixed your problem. I have just answered. – Mat Oct 20 '16 at 07:45
  • A cleaner, Swift 4 approach can be found in my answer [here](https://stackoverflow.com/questions/35676407/nstimer-not-stopping-when-invalidated-in-this-function). – Apps-n-Add-Ons Nov 05 '17 at 11:46

4 Answers4

122

Try to make the following changes to your code:

First, you have to change the way you declare timerTest

var timerTest : Timer?

then in startTimer before instantiating check if timerTest is nil

func startTimer () {
  guard timerTest == nil else { return }

  timerTest =  Timer.scheduledTimer(
      timeInterval: TimeInterval(0.3),
      target      : self,
      selector    : #selector(ViewController.timerActionTest),
      userInfo    : nil,
      repeats     : true)
}

Finally in your stopTimerTest you invalidate timerTest if it isn't nil

func stopTimerTest() {
  timerTest?.invalidate()
  timerTest = nil
}
Mark
  • 1,258
  • 13
  • 25
Mat
  • 6,236
  • 9
  • 42
  • 55
44

Most likely you've called startTimer twice without calling stopTimerTest. If you do that, you'll lose your pointer to the original timer and never be able to invalidate it.

The typical approach is to manage invalidation as a part of setting:

var timerTest : Timer? = nil {
    willSet {
        timerTest?.invalidate()
    }
}

Then stopping is just setting to nil:

func stopTimerTest() {
    timerTest = nil
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
11

Make sure when you call StartTimer it is nil and if you call StartTimer twice without calling StopTimer. You will lose your original pointer and you can't stop it.

 var timer : Timer? = nil {
        willSet {
            timer?.invalidate()
        }
    }

Start and Stop timer like ...

func startTimer() {
    stopTimer()
    guard self.timer == nil else { return }
    self.timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.fetchData), userInfo: nil, repeats: true)
}

func stopTimer() {
    guard timer != nil else { return }
    timer?.invalidate()
    timer = nil
}
Sandeep Maurya
  • 1,979
  • 17
  • 22
5

Check, are you really call stopTimerTest(), because timerTest.invalidate() is correct for stopping timer.

func stopTimerTest() {
    print("stopTimer")
    timerTest.invalidate()
}
Igor
  • 12,165
  • 4
  • 57
  • 73