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
How to Implement a Swiping/Sliding Animation Between Views
Open Link to Facebook Page from iOS App
Silent Push Notifications Only Delivered If Device Is Charging And/Or App Is Foreground
Fetching All Contacts in iOS Swift
Uitextview Style Is Being Reset After Setting Text Property
Scale Image in an Uibutton to Aspectfit
Xcode 8.3/Xcode 9.0 Refresh Provisioning Profile Devices
Is There a Public Way to Force Mpnowplayinginfocenter to Show Podcast Controls
Completion Handler for Uinavigationcontroller "Pushviewcontroller:Animated"
Notificationcenter Issue on Swift 3
How to Draw Border Around a Uilabel
Xcode 8/Swift 3: "Expression of Type Uiviewcontroller? Is Unused" Warning
Handling Touch Event in Uilabel and Hooking It Up to an Ibaction
How to Get the Height and Width of an Uiimage
Atos Cannot Get Symbols from Dsym of Archived Application
How to Add a Image in Email Body Using Mfmailcomposeviewcontroller