291

I tried

var timer = NSTimer()
timer(timeInterval: 0.01, target: self, selector: update, userInfo: nil, repeats: false)

But, I got an error saying

'(timeInterval: $T1, target: ViewController, selector: () -> (), userInfo: NilType, repeats: Bool) -> $T6' is not identical to 'NSTimer'
vacawama
  • 150,663
  • 30
  • 266
  • 294
user3225917
  • 2,991
  • 2
  • 13
  • 17

17 Answers17

561

This will work:

override func viewDidLoad() {
    super.viewDidLoad()
    // Swift block syntax (iOS 10+)
    let timer = Timer(timeInterval: 0.4, repeats: true) { _ in print("Done!") }
    // Swift >=3 selector syntax
    let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
    // Swift 2.2 selector syntax
    let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: #selector(MyClass.update), userInfo: nil, repeats: true)
    // Swift <2.2 selector syntax
    let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}

// must be internal or public. 
@objc func update() {
    // Something cool
}

For Swift 4, the method of which you want to get the selector must be exposed to Objective-C, thus @objc attribute must be added to the method declaration.

Simon Bengtsson
  • 7,573
  • 3
  • 58
  • 87
Oscar Swanros
  • 19,767
  • 5
  • 32
  • 48
  • 2
    I'd add that the class with these methods needs to be an NSObject, else you end up with an unrecognised selector error – Joshua Jul 11 '14 at 07:00
  • 29
    As of Xcode 6.1, I had to add "@objc" to the function header like this: "@objc func update() {". Without it the app crashes upon the first fire. – kev Nov 20 '14 at 01:19
  • You can declare Var timer: NSTimer! initially and use it whenever needed! – Nigilan Sep 01 '15 at 07:38
  • 2
    A perhaps more useful version of the block syntax: let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { _ in print("Done.") } – Teo Sartori Jan 04 '19 at 14:53
  • 2
    You can't use 'let timer = Timer(timeInterval: 0.4, repeats: true) { _ in print("Done!") }' this will not start the timer and then you cannot get it to repeat. You must use Timer.scheduledTimer. – Siamaster Mar 29 '19 at 15:28
  • Timer needs to be scheduled on the main thread – Michael N Jan 03 '21 at 09:08
  • How is this upvoted when Timer(timeInterval: 0.4, repeats: true) does not actually do anything? – Anton Duzenko Sep 19 '21 at 09:53
168

Repeated event

You can use a timer to do an action multiple times, as seen in the following example. The timer calls a method to update a label every half second.

enter image description here

Here is the code for that:

import UIKit

class ViewController: UIViewController {

    var counter = 0
    var timer = Timer()

    @IBOutlet weak var label: UILabel!

    // start timer
    @IBAction func startTimerButtonTapped(sender: UIButton) {
        timer.invalidate() // just in case this button is tapped multiple times

        // start the timer
        timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
    }

    // stop timer
    @IBAction func cancelTimerButtonTapped(sender: UIButton) {
        timer.invalidate()
    }

    // called every time interval from the timer
    func timerAction() {
        counter += 1
        label.text = "\(counter)"
    }
}

Delayed event

You can also use a timer to schedule a one time event for some time in the future. The main difference from the above example is that you use repeats: false instead of true.

timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)

The above example calls a method named delayedAction two seconds after the timer is set. It is not repeated, but you can still call timer.invalidate() if you need to cancel the event before it ever happens.

Notes

  • If there is any chance of starting your timer instance multiple times, be sure that you invalidate the old timer instance first. Otherwise you lose the reference to the timer and you can't stop it anymore. (see this Q&A)
  • Don't use timers when they aren't needed. See the timers section of the Energy Efficiency Guide for iOS Apps.

Related

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
34

Updated to Swift 4, leveraging userInfo:

class TimerSample {

    var timer: Timer?

    func startTimer() {
        timer = Timer.scheduledTimer(timeInterval: 5.0,
                                     target: self,
                                     selector: #selector(eventWith(timer:)),
                                     userInfo: [ "foo" : "bar" ],
                                     repeats: true)
    }

    // Timer expects @objc selector
    @objc func eventWith(timer: Timer!) {
        let info = timer.userInfo as Any
        print(info)
    }

}
igraczech
  • 2,408
  • 3
  • 25
  • 30
  • 2
    Show a working example, what does "custom" and "data" mean if function is expecting a `NSTimer` object – Carlos.V Sep 05 '16 at 23:01
  • 1
    It really does not matter. You're free to store anything you need into the userInfo dictionary, in this case it is arbitrary key-value pair. – igraczech Jan 19 '17 at 09:04
  • 1
    This is useful, but broke in Swift 3, working example: Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(event), userInfo: "Info Sent", repeats: true) – Bobby Apr 15 '17 at 22:15
33

As of iOS 10 there is also a new block based Timer factory method which is cleaner than using the selector:

    _ = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
        label.isHidden = true
    }
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
  • 1
    The way you're doing it, wouldn't it be better to just remove the `_ = ` and just begin with `Timer`? – mfaani Jul 31 '17 at 23:03
  • 2
    You can omit the _ = if you silence the warning about the unused value or if you just don't care about warnings. I do not like to check in code with warnings. – Josh Homann Aug 01 '17 at 23:46
30

Swift 5

I personally prefer the Timer with the block closure:

    Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { (_) in
       // TODO: - whatever you want
    }
Wissa
  • 1,444
  • 20
  • 24
24

Swift 3, pre iOS 10

func schedule() {
    DispatchQueue.main.async {
      self.timer = Timer.scheduledTimer(timeInterval: 20, target: self,
                                   selector: #selector(self.timerDidFire(timer:)), userInfo: nil, repeats: false)
    }
  }

  @objc private func timerDidFire(timer: Timer) {
    print(timer)
  }

Swift 3, iOS 10+

DispatchQueue.main.async {
      self.timer = Timer.scheduledTimer(withTimeInterval: 20, repeats: false) { timer in
        print(timer)
      }
    }

Notes

  • It needs to be on the main queue
  • Callback function can be public, private, ...
  • Callback function needs to be @objc
onmyway133
  • 45,645
  • 31
  • 257
  • 263
  • 1
    My understanding is that only the timer callback should be on the main queue and that the following would be slightly more efficient: self.timer = Timer.scheduledTimer(withTimeInterval: 20, repeats: false) { timer in DispatchQueue.main.async { print(timer) } } – Mathieu Frenette Feb 21 '17 at 15:42
  • My timer wasn't triggering from one of my Objects and that made the trick :) – Reimond Hill Mar 15 '18 at 14:40
  • @ReimondHill You need to change `timeInterval` – onmyway133 Aug 02 '18 at 07:33
20

Check with:

Swift 2

var timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("update"), userInfo: nil, repeats: true)

Swift 3, 4, 5

var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
Karen Hovhannisyan
  • 1,140
  • 2
  • 21
  • 31
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
  • 2
    I already tried it but it says 'Could not find an overload for 'init' that accepts the supplied arguments' – user3225917 Jun 03 '14 at 05:17
  • 1
    Same here, I got the error 'Could not find an overload for 'init' that accepts the supplied arguments'. Does this line really work? – Yangshun Tay Jun 04 '14 at 06:05
  • I get the same error as @yangshun. What type of object must `self` be? UIView is ok? – SimplGy Jun 10 '14 at 02:19
  • @SimpleAsCouldBe: yes that is ok – Midhun MP Jun 10 '14 at 04:05
  • func amountSubmitSuccess() { self.view.hideToastActivity() self.view.makeToast(message: "The Amount Successfully Registered") var timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "moveToBidderPage", userInfo: nil, repeats: false) } func moveToBidderPage () { let loginPageView = self.storyboard?.instantiateViewControllerWithIdentifier("bidderpageID") as! BidderPage self.navigationController?.pushViewController(loginPageView, animated: true) } – Alvin George Oct 07 '15 at 07:44
  • Perhaps try passing `"update"` instead of `Selector("update")`? – Nicolas Miari Nov 11 '15 at 07:35
13

You will need to use Timer instead of NSTimer in Swift 3.

Here is an example:

Timer.scheduledTimer(timeInterval: 1, 
    target: self, 
    selector: #selector(YourController.update), 
    userInfo: nil, 
    repeats: true)

// @objc selector expected for Timer
@objc func update() {
    // do what should happen when timer triggers an event
}
dub stylee
  • 3,252
  • 5
  • 38
  • 59
Ondrej Kvasnovsky
  • 4,592
  • 3
  • 30
  • 40
10

First declare your timer

var timer: Timer?

Then add line in viewDidLoad() or in any function you want to start the timer

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(action), userInfo: nil, repeats: false)

This is the func you will callback it to do something it must be @objc

@objc func action () {
print("done")
}
Gerges Eid
  • 1,031
  • 12
  • 18
8

for swift 3 and Xcode 8.2 (nice to have blocks, but if You compile for iOS9 AND want userInfo):

...

        self.timer = Timer(fireAt: fire,
                           interval: deltaT,
                           target: self,
                           selector: #selector(timerCallBack(timer:)),
                           userInfo: ["custom":"data"],
                           repeats: true)

        RunLoop.main.add(self.timer!, forMode: RunLoopMode.commonModes)
        self.timer!.fire()
}

func timerCallBack(timer: Timer!){
        let info = timer.userInfo
        print(info)
    }
ingconti
  • 10,876
  • 3
  • 61
  • 48
6

SimpleTimer (Swift 3.1)

Why?

This is a simple timer class in swift that enables you to:

  • Local scoped timer
  • Chainable
  • One liners
  • Use regular callbacks

Usage:

SimpleTimer(interval: 3,repeats: true){print("tick")}.start()//Ticks every 3 secs

Code:

class SimpleTimer {/*<--was named Timer, but since swift 3, NSTimer is now Timer*/
    typealias Tick = ()->Void
    var timer:Timer?
    var interval:TimeInterval /*in seconds*/
    var repeats:Bool
    var tick:Tick

    init( interval:TimeInterval, repeats:Bool = false, onTick:@escaping Tick){
        self.interval = interval
        self.repeats = repeats
        self.tick = onTick
    }
    func start(){
        timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(update), userInfo: nil, repeats: true)//swift 3 upgrade
    }
    func stop(){
        if(timer != nil){timer!.invalidate()}
    }
    /**
     * This method must be in the public or scope
     */
    @objc func update() {
        tick()
    }
}
Sentry.co
  • 5,355
  • 43
  • 38
3
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)

And Create Fun By The Name createEnemy

fund createEnemy ()
{
do anything ////
}
dkackman
  • 15,179
  • 13
  • 69
  • 123
2

In Swift 3 something like this with @objc:

func startTimerForResendingCode() {
    let timerIntervalForResendingCode = TimeInterval(60)
    Timer.scheduledTimer(timeInterval: timerIntervalForResendingCode,
                         target: self,
                         selector: #selector(timerEndedUp),
                         userInfo: nil,
                         repeats: false)
}




@objc func timerEndedUp() {
    output?.timerHasFinishedAndCodeMayBeResended()
}
Raghav
  • 114
  • 2
  • 12
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
2

I tried to do in a NSObject Class and this worked for me:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {  
print("Bang!") }
Álvaro Agüero
  • 4,494
  • 1
  • 42
  • 39
  • That is a way to add a task to the main thread's DispatchQueue for later execution, but it isn't the same thing as creating a Timer. +1 for offering an alternative (voted), but you should edit your answer to say something like "In addition to creating a timer, you can also set up code to fire after a delay using the DispatchQueue method `asyncAfter()`. That would look something like this:" – Duncan C Jun 30 '21 at 02:15
1

If you init method of timer

let timer = Timer(timeInterval: 3, target: self, selector: #selector(update(_:)), userInfo: [key : value], repeats: false)

func update(_ timer : Timer) {

}

then add it to loop using method other selector will not be called

RunLoop.main.add(timer!, forMode: .defaultRunLoopMode)

NOTE : If you are want this to repeat make repeats true and keep the reference of timer otherwise update method will not be called.

If you are using this method.

Timer.scheduledTimer(timeInterval: seconds, target: self, selector: #selector(update(_:)), userInfo: nil, repeats: true)

keep a reference for later use if repeats is true.

Surjeet Rajput
  • 1,251
  • 17
  • 24
1

Below is an example (for Swift 5.x) of a basic Timer object created when a UIButton is pressed. It uses a closure to print the current timer value to the log and eventually calls invalidate() to stop and remove the Timer from the run loop.

@IBAction func startTimer(_ sender: UIButton) {
    var runCount = 60 //for a 60 second timer
    
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
        print(runCount)
        runCount -= 1

        if runCount < 0 {
            timer.invalidate()
        }
    }
    
}
jerusso
  • 11
  • 2
-2

NSTimer has been renamed to Timer in Swift 4.2. this syntax will work in 4.2:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UIMenuController.update), userInfo: nil, repeats: true)