Do something every x minutes in Swift

How can I run a function every minute? In JavaScript I can do something like setInterval, does something similar exist in Swift?

Wanted output:

Hello World once a minute...

108028 次浏览

You can use Timer (swift 3)

var timer = Timer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("function"), userInfo: nil, repeats: true)

In selector() you put in your function name

var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)


func sayHello()
{
NSLog("hello World")
}

Remember to import Foundation.

Swift 4:

 var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)


@objc func sayHello()
{
NSLog("hello World")
}

If targeting iOS version 10 and greater, you can use the block-based rendition of Timer, which simplifies the potential strong reference cycles, e.g.:

weak var timer: Timer?


func startTimer() {
timer?.invalidate()   // just in case you had existing `Timer`, `invalidate` it before we lose our reference to it
timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
// do something here
}
}


func stopTimer() {
timer?.invalidate()
}


// if appropriate, make sure to stop your timer in `deinit`


deinit {
stopTimer()
}

While Timer is generally best, for the sake of completeness, I should note that you can also use dispatch timer, which is useful for scheduling timers on background threads. With dispatch timers, since they're block-based, it avoids some of the strong reference cycle challenges with the old target/selector pattern of Timer, as long as you use weak references.

So:

var timer: DispatchSourceTimer?


func startTimer() {
let queue = DispatchQueue(label: "com.domain.app.timer")  // you can also use `DispatchQueue.main`, if you want
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline: .now(), repeating: .seconds(60))
timer!.setEventHandler { [weak self] in
// do whatever you want here
}
timer!.resume()
}


func stopTimer() {
timer = nil
}

For more information, see the the Creating a Timer section of Dispatch Source Examples in the Dispatch Sources section of the Concurrency Programming Guide.


For Swift 2, see previous revision of this answer.

In swift 3.0 the GCD got refactored:

let timer : DispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)


timer.scheduleRepeating(deadline: .now(), interval: .seconds(60))
timer.setEventHandler
{
NSLog("Hello World")
}
timer.resume()

This is specially useful for when you need to dispatch on a particular Queue. Also, if you're planning on using this for user interface updating, I suggest looking into CADisplayLink as it's synchronized with the GPU refresh rate.

Here's an update to the NSTimer answer, for Swift 3 (in which NSTimer was renamed to Timer) using a closure rather than a named function:

var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
(_) in
print("Hello world")
}

If you can allow for some time drift here's a simple solution executing some code every minute:

private func executeRepeatedly() {
// put your code here


DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
self?.executeRepeatedly()
}
}

Just run executeRepeatedly() once and it'll be executed every minute. The execution stops when the owning object (self) is released. You also can use a flag to indicate that the execution must stop.

Here is another version algrid's answer with an easy way to stop it

@objc func executeRepeatedly() {


print("--Do something on repeat--")
        

perform(#selector(executeRepeatedly), with: nil, afterDelay: 60.0)
}

Here's an example of how to start it and stop it:

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)


executeRepeatedly() // start it
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)


NSObject.cancelPreviousPerformRequests(withTarget: self) // stop it
}
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true, block: myMethod)


func myMethod(_:Timer) {
...
}

or

timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
...
}

make sure to invalid the timer at some point like your time is no longer visible, or you object is deist