Timer.scheduledTimer does not work in Swift 3
Solution 1
I found that creating the timer in an OperationQueue Operation did not work. I assume this is because there is no runloop.
Therefore, the following code fixed my problem:
DispatchQueue.main.async {
// timer needs a runloop?
self.timeoutTimer = Timer.scheduledTimer(timeInterval: self.timeout, target: self, selector: #selector(self.onTimeout(_:)), userInfo: nil, repeats: false)
}
Solution 2
Timer methods with a selector are supposed to have one parameter: The timer itself. Thus your code should really look like this: 1
Timer.scheduledTimer(timeInterval: 1.1,
target: self,
selector: #selector(self.adjustmentBestSongBpmHeartRate(_:),
userInfo: nil,
repeats: false)
@objc func adjustmentBestSongBpmHeartRate(_ timer: Timer) {
print("frr")
}
Note that if your app only runs on iOS >= 10, you can use the new method that takes a block to invoke rather than a target/selector. Much cleaner and more type-safe:
class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: @escaping (Timer) -> Void) -> Timer
That code would look like this:
timer = Timer.scheduledTimer(withTimeInterval: 1.1,
repeats: false) {
timer in
//Put the code that be called by the timer here.
print("frr")
}
Note that if your timer block/closure needs access to instance variables from your class you have to take special care with self
. Here's a good pattern for that sort of code:
timer = Timer.scheduledTimer(withTimeInterval: 1.1,
repeats: false) {
//"[weak self]" creates a "capture group" for timer
[weak self] timer in
//Add a guard statement to bail out of the timer code
//if the object has been freed.
guard let strongSelf = self else {
return
}
//Put the code that be called by the timer here.
print(strongSelf.someProperty)
strongSelf.someOtherProperty = someValue
}
Edit (updated 15 December)
1: I should add that the method you use in the selector has to use Objective-C dynamic dispatch. In Swift 4 and later, the individual methods you reference must be tagged with the @objc
tag. In previous versions of Swift you could also declare the entire class that defines the selector with the @objc
qualifier, or you could make the class that defined the selector a subclass of NSObject
or any class that inherits from NSOBject
. (It's quite common to define the method the timer calls inside a UIViewController
, which is a subclass of NSObject
, so it used to "just work".
Solution 3
Swift 3
In my case it worked after I added to my method the @obj prefix
Class TestClass {
private var timer: Timer?
func start() {
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(handleMyFunction), userInfo: nil, repeats: false)
}
func stop() {
guard timer != nil else { return }
timer?.invalidate()
timer = nil
}
@objc func handleMyFunction() {
// Code here
}
}
Solution 4
Try this -
if #available(iOS 10.0, *) {
self.timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
self.update()
})
} else {
self.timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.update), userInfo: nil, repeats: false)
}
Mostly the problem must have been because of iOS version of mobile.
Solution 5
Swift 5, Swift 4 Simple way only call with Dispatch Queue Async
DispatchQueue.main.async
{
self.andicator.stopAnimating()
self.bgv.isHidden = true
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { _ in
obj.showAlert(title: "Successfully!", message: "Video save successfully to Library directory.", viewController: self)
})
}
Related videos on Youtube
Jing Bian
Updated on July 09, 2022Comments
-
Jing Bian almost 2 years
I want to call the method
func adjustmentBestSongBpmHeartRate()
every 1.1 second. I used Timer, but it doesn't work. I have read the document and found a lot of sample code, it still does work! Is there anything I missed?timer = Timer.scheduledTimer(timeInterval: 1.1, target: self, selector: #selector(self.adjustmentBestSongBpmHeartRate), userInfo: nil, repeats: false) timer.fire() func adjustmentBestSongBpmHeartRate() { print("frr") }
-
Marie Dm over 7 yearsI don't think you need to call fire()
-
Jing Bian over 7 yearsI changed
repeats: false
torepeats: true
, it still doesn't work. -
Marie Dm over 7 yearsFrom where do you call it?
-
Jing Bian over 7 yearsI also deleted
timer.fire()
, no use.... -
Jing Bian over 7 yearsI'm calling it from
override func viewDidLoad()
method -
Marie Dm over 7 yearsMaybe you should try from viewDidAppear?
-
Jing Bian over 7 yearsthank you ! It worked after I moved it to viewWillAppear
-
Marie Dm over 7 yearsGlad for you! Don't hesitate to upvote my comment ;)
-
-
Alfishe over 7 yearsIt's related to thread's loop assignment. You can manage that by manually adding timer to processing loop (like in example: hackingwithswift.com/example-code/system/…). In my case i used dispatch in main queue and it started to work. Weird thing
-
Andy over 7 yearsThe same thing happened to me - i was invalidating the timer in viewDidDisappear, but setting it up in viewDidLoad, so the second time the view gets shown, the timer doesnt start
-
Duncan C about 7 yearsGood point. The method the timer invokes needs to use Objective-C messaging.
-
Tom Spee over 6 yearsAnd what if your
handleMyFunction
has a completion like:completion: @escaping (String) -> (Void)
? That is throwing an error for me. -
Duncan C over 6 years@TomSpee, you can't do that. Timers can only have no parameters, or a single parameter which is the timer itself. You might be able to pass a completion handler in as the userInfo to the timer by casting it to type
id
, but I'm not sure. -
Fidel López over 5 yearsMy case.. Seems obvious now. Thanks!
-
Rameez over 4 yearsAnd i was scheduling timer in background thread. Moved this timer scheduling from background thread to main thread and its started working as i expected it to be. Thanks man.
-
Benjamin B. about 3 yearsThis should be the accepted answer if your timer is using a closure...if your closure is not executing, make it async...