Timer.scheduledTimer does not work in Swift 3

36,779

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)

   })
}
Share:
36,779

Related videos on Youtube

Jing Bian
Author by

Jing Bian

Updated on July 09, 2022

Comments

  • Jing Bian
    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
      Marie Dm over 7 years
      I don't think you need to call fire()
    • Jing Bian
      Jing Bian over 7 years
      I changed repeats: false to repeats: true, it still doesn't work.
    • Marie Dm
      Marie Dm over 7 years
      From where do you call it?
    • Jing Bian
      Jing Bian over 7 years
      I also deleted timer.fire(), no use....
    • Jing Bian
      Jing Bian over 7 years
      I'm calling it from override func viewDidLoad() method
    • Marie Dm
      Marie Dm over 7 years
      Maybe you should try from viewDidAppear?
    • Jing Bian
      Jing Bian over 7 years
      thank you ! It worked after I moved it to viewWillAppear
    • Marie Dm
      Marie Dm over 7 years
      Glad for you! Don't hesitate to upvote my comment ;)
  • Alfishe
    Alfishe over 7 years
    It'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
    Andy over 7 years
    The 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
    Duncan C about 7 years
    Good point. The method the timer invokes needs to use Objective-C messaging.
  • Tom Spee
    Tom Spee over 6 years
    And what if your handleMyFunction has a completion like: completion: @escaping (String) -> (Void)? That is throwing an error for me.
  • Duncan C
    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
    Fidel López over 5 years
    My case.. Seems obvious now. Thanks!
  • Rameez
    Rameez over 4 years
    And 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.
    Benjamin B. about 3 years
    This should be the accepted answer if your timer is using a closure...if your closure is not executing, make it async...