How to Make a Function Execute Every Second in Swift

How to execute a method every second on a background thread so it doesn't affect the performance of the app

Use Grand Central Dispatch :

DispatchQueue.global(qos: .background).async {
getDatabaseInfo()
}

Swift: return value every x seconds

A possible solution is to use the Timer from the Combine framework:

struct ContentView: View {
@State private var text = "initial text"
@State private var timer: AnyCancellable?

var body: some View {
Text(text)
Button(action: startTimer) { // start timer on button tap, alternatively put it in `onAppear`
Text("Start timer")
}
}

func startTimer() {
// start timer (tick every 10 seconds)
timer = Timer.publish(every: 10, on: .main, in: .common)
.autoconnect()
.sink { _ in
text = DataGenerator.instance.randomString(of: 5)
}
}
}

You also need to return the String from the randomString function. A good thing would also be to rename Data to avoid collisions:

class DataGenerator { // rename the class
static let instance = DataGenerator()

func randomString(of length: Int) -> String { // return `String`
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var s = ""
for _ in 0 ..< length {
s.append(letters.randomElement()!)

print("\(s) = I'm in randomString Func")
}
return s // return String, don't call `DispatchQueue.main.async` here
}
}

You may also consider moving the timer logic out of the view like in:

  • How to make the View update instant in SwiftUI?

How to execute a function for 5 seconds in swift

Sticking with SpriteKit I would just do the following

func shrinkShip(duration: Double) { 

let originalSize = spaceShip.size

spaceShip.scale(to: CGSize(width: 60, height: 40))

for x in 0..<Int(duration) {

//increment the timer every 1 second for the duration
self.run(SKAction.wait(forDuration: 1 * Double(x))) {
self.timerLabel.text = String(Int(duration) - x)
}
}
self.run(SKAction.wait(forDuration: duration)) {
//return the spaceship to full size
self.spaceShip.scale(to: originalSize)
//hide the timer
self.timerLabel.text = ""
self.timerLabel.isHidden = true
}
}

Do something every x minutes in Swift

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")
}

How to call Swift function at specified date/time

If you make a GCD timer with makeTimerSource with a flag of .strict, it will opt out of timer coalescing:

The system makes its best effort to observe the timer’s specified leeway value, even if the value is smaller than the default leeway.

For example:

var timer: DispatchSourceTimer?

func startTimer(in interval: DispatchTimeInterval, block: @escaping () -> Void) {
timer = DispatchSource.makeTimerSource(flags: .strict, queue: .main)
timer?.setEventHandler(handler: block)
timer?.schedule(deadline: .now() + interval, leeway: .milliseconds(500))
timer?.activate()
}

And then:

startTimer(in: .seconds(240)) { [weak self] in
self?.foo()
}

Note, unlike a Timer, which is retained by the RunLoop upon which you schedule it, you have to keep your own strong reference to the GCD timer, as shown above. You might make Timer properties weak (so that they are deallocated when the RunLoop releases them), but you want strong references to GCD timers.

A few caveats:

  • Note the use of [weak self] capture list. You generally want to avoid strong reference cycle (especially if the timer was a repeating timer).

  • This pattern should be employed only when absolutely needed, as you are opting out of the power saving features offered by timer coalescing. I would expect that this is more power efficient than the repeating timer approach, but less power efficient than letting the OS use its discretion to coalesce its timers.

  • This pattern assumes that the queue on which it is scheduled is not blocked. Obviously, we never block the main thread, but you can always consider using your own target queue.

  • For the sake of future readers, this pattern obviously applies when the app is actually running. If the app is suspended, timers cannot fire. If you want the user to be notified at the appointed time even if the app is not currently running, you would use a locally scheduled user notification.

How to call a function every 3 seconds using Swift Spritekit

Well, you could build it pretty easily using SKActions:

    let pauser = SKAction.wait(forDuration: 3.0)
let trigger = SKAction.perform(#selector(GameViewController.hideFunc, onTarget: self)
let pauseThenTrigger = SKAction.sequence([ pauser, trigger ])
let repeatForever = SKAction.repeatForever(pauseThenTrigger)
node.run( repeatForever )

This approach would have the virtue of letting you reuse this action for any nodes that would need to use it, and you can easily control the action by removing or adding the node from the scene, calling removeAllActions() on it, etc.



Related Topics



Leave a reply



Submit