I\'ve been searching for a way to use a Timer in Swift 4 and looked at this article. I test out my code in xcode and when the the timer first ticks (in this case after 10 se
Try this in swift 4
import UIKit
class ViewController: UIViewController {
var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.updateValue), userInfo: nil, repeats: true)
}
func updateValue(){
print("Timer is call")
}
}
Your timer is initialized in the wrong way because:
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values.
moreover:
You also cannot use the implicit self property, or call any of the instance’s methods (See here)
hence, to fix your problem, you should move such code inside viewDidLoad
. So you may try this:
import UIKit
class ViewController: UIViewController {
weak var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(withTimeInterval: 10, repeats: true, block: { [weak self] timer in
self?.timer = timer
self?.tick()
})
}
deinit {
self.timer?.invalidate()
}
@objc func tick() {
print("tick")
}
}
As Andrea said, you should instantiate the timer in viewDidLoad
. Also the documentation says:
The selector should have the following signature:
timerFireMethod:
(including a colon to indicate that the method takes an argument).
And don't forget to disable this timer in, for example, viewDidDisappear
. You can't invalidate
it in deinit
because the repeating timer keeps a strong reference to its target, and your deinit
will not get called as long as the timer is running. And if you remove it in viewDidDisappear
, you might want to create the timer in viewDidAppear
.
Thus, resulting in something like:
class ViewController: UIViewController {
weak var timer: Timer?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(tick(_:)), userInfo: nil, repeats: true)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
timer?.invalidate()
}
@objc func tick(_ timer: Timer) {
print("tick")
}
}
Or you can use the block-based rendition, with [weak self]
pattern, and the timer won't keep a strong reference to the view controller, in which case you can use deinit
:
class ViewController: UIViewController {
var timer: Timer?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] timer in // the `[weak self] reference is only needed if you reference `self` in the closure
print("tick")
}
}
deinit {
timer?.invalidate()
}
}