Context
I am beginner learning Swift and I'm trying to make sure I understand this bug "fix". I followed along a Youtube video showing how to create a timer in Xcode. I changed some things around in effort to learn some things but the issue that arose was from the original code.
The Bug
Every time I started the simulator, if I pressed the Stop or Reset button without first starting the timer, the app would crash and show this error on the same line as timer.invalidate()
:
Unexpectedly found nil while implicitly unwrapping an Optional value
My Fix
To fix the issue I added ?
to make it timer?.invalidate()
as seen in the code below. I tried this after some Googling to get a very loose understanding of what is going on.
Did this work because the timer did not exist yet and therefore it was "nil"?
Is this the best way to prevent the crash? It seems that I could also change the variable timer to use ?
instead and have it work, but only after adding ?
to the timer.invalidate() line in the step function as well as the others. Clearly I need to brush up on my understanding of Optionals.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
var timeRemaining: Int = 10
var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func start(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
}
@IBAction func stop(_ sender: Any) {
timer?.invalidate() //added ? to timer
}
@IBAction func reset(_ sender: Any) {
timer?.invalidate() //added ? to timer
timeRemaining = 10
label.text = "\(timeRemaining)"
}
@objc func step() {
if timeRemaining > 0 {
timeRemaining -= 1
} else {
timer.invalidate()
}
label.text = "\(timeRemaining)"
}
}
var timer: Timer!
Means that you're telling the compiler that you are sure that timer
will never be nil
, so if you try to use the variable and it is nil you got a crash. This is probably what happened to you: a call to reset(_:)
or stop(_:)
before a start(_:)
.
timer?.invalidate() //added ? to timer
fix the crash because now you are accessing timer
through optional chaining.
But it does not solve the potential issue everywhere so declaring:
var timer: Timer?
is safer