DispatchSourceTimer and Swift 3.0 - swift

I can't figure out how to make dispatch timer work repeatedly in Swift 3.0. My code:
let queue = DispatchQueue(label: "com.firm.app.timer",
attributes: DispatchQueue.Attributes.concurrent)
let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)),
queue: queue)
timer.scheduleRepeating(deadline: DispatchTime.now(),
interval: .seconds(5),
leeway: .seconds(1)
)
timer.setEventHandler(handler: {
//a bunch of code here
})
timer.resume()
Timer just fires one time and doesn't repeat itself like it should be. How can I fix this?

Make sure the timer doesn't fall out of scope. Unlike Timer (where the RunLoop on which you schedule it keeps the strong reference until the Timer is invalidated), you need to maintain your own strong reference to your GCD timers, e.g.:
private var timer: DispatchSourceTimer?
private func startTimer() {
let queue = DispatchQueue(label: "com.firm.app.timer", attributes: .concurrent)
timer = DispatchSource.makeTimerSource(queue: queue)
timer?.setEventHandler { [weak self] in // `[weak self]` only needed if you reference `self` in this closure and you want to prevent strong reference cycle
print(Date())
}
timer?.schedule(deadline: .now(), repeating: .seconds(5), leeway: .milliseconds(100))
timer?.resume()
}
private func stopTimer() {
timer = nil
}

Related

Dispatch Source timer schedule timout

With the following Swift code, I'm trying to create a task that runs every hour:
let queue: DispatchQueue = .main
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now(), repeating: .seconds(3600), leeway: .milliseconds(100)
timer.setEventHandler { [weak self] in
// run code
}
Now, when I have the repeating set at a lower number, say 10 or event 150 seconds, it triggers as expected both in the foreground and background (or, rather, once the foreground hits it triggers, if the timer went off while in the background).
However, when I let the app timeout to the lock screen, and wait for an hour, it doesn't display.
Is there some timeout that Apple has for DispatchSource schedules? If so, what is it? And is there any way to change or get around it?
Edit
I don't want special functionality when it backgrounds, I want the code to keep running as normal and to trigger the event handler when the timeout happens, even if it's in the background
I ended up taking matt's suggestion and saving the time every time the code gets called, as seen below. Worked well!
let timeOfLastCheck = Date()
let queue: DispatchQueue = .main
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now(), repeating: .seconds(3600), leeway: .milliseconds(100)
timer.setEventHandler { [weak self] in
timeOfLastCheck = Date()
// run code
}
And elsewhere, where the timer is actually getting created:
let notificationCenter: NotificationCenter = .default
let activeNotificationToken = notificationCenter.addObserver(
forName: UIApplication.didBecomeActiveNotification,
object: nil,
queue: nil
) { [weak self] _ in
let now = Date()
if let `self` = self,
let timeInterval = TimeInterval(dispatchTimeInterval: self.interval), // TimeInterval is extended elsewhere to be able to take in a DispatchTimeInterval in the init
now > timeOfLastCheck.addingTimeInterval(timeInterval) {
self.timeOfLastCheck = Date()
// run code
}
}

How to add DispatchQueue delay in swift while loop?

I'm trying to create a delay inside a while loop. I'm fairly new to this and it's currently just not working. It never fires even once with the dispatch delay, but if I remove the delay it fires repeatedly.
Basically what I'm doing is checking if the velocity of nodes in a SKScene is still moving, if they're still moving, don't end the game. But once they've slowed down, end the game.
func RemainingNodeCheck (complete:() -> Void) {
CountVelocites()
if (IdleVelocity.max()!.isLess(than: 1.0)) {
complete()
} else {
print("Velocity too high, begin wait...")
while !(IdleVelocity.max()?.isLess(than: 1.0))! {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(1)) {
print("Second passed")
self.CountVelocites()
}
if (IdleVelocity.max()!.isLess(than: 1.0)) {
break
}
}
print("Velocity Calmed down")
complete()
}
}
I believe this might be something to do with threads? Or it's actually just telling the delay to begin waiting for one second so many times that it never gets to call?
UPDATE: I would use a timer, but the RemaingNodeCheck is being called from another part and it's waiting for RemainingNodeCheck to send back complete()
You never want to "wait". But you can set up a repeating timer that checks for some condition, and if so, calls the complete closure (invalidating the timer, if you want). E.g.
class ViewController: UIViewController {
var idleVelocity: ...
weak var timer: Timer?
deinit {
timer?.invalidate()
}
func startCheckingVelocity(complete: #escaping () -> Void) {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self = self, let maxVelocity = self.idleVelocity.max() else { return }
if maxVelocity < 1 {
timer.invalidate()
complete()
return
}
print("velocity too high...")
}
}
}

Why does timer continue to execute after invalidation?

If you run the code below, even after I invalidate the timer, the remaining code of the timer executes without any disruption. Why?
Is it because the closure has a strong reference to itself and is retained until it completely finishes itself? Or something else?
Does this mean invalidating a timer during its moment of execution does nothing?
class ViewController: UIViewController {
var timer : Timer?
let serialQueue = DispatchQueue(label: "com.createTimer.serial")
override func viewDidLoad() {
super.viewDidLoad()
serialQueue.sync { [weak self] in
self?.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: { [weak self] _ in
self?.someDummyFunc()
print("yyy")
print("\(self?.timer?.isValid)")
})
}
}
func someDummyFunc(){
print("\(timer?.isValid)")
print("xxx")
timer?.invalidate()
}
}
The prints that I get from running this code is:
Optional(true)
xxx
yyy
Optional(false) // timer.isValid is false !!!
Yet what I initially thought I would get is:
Optional(true)
xxx
The scheduledTimer(withTimeInterval:repeats:block:) method:
After interval seconds have elapsed, the timer fires, executing block.
The invalidate() method:
Stops the timer from ever firing again
You are correct in your discovery that invalidating a timer will not interrupt a currently executing block, but will only prevent future executions of that block.
Timer or not, any enqueued block will have to finish. Once it's enqueued there's no stopping it.
Suppose you had the following code inside a viewController's:
override func viewDidLoad() {
super.viewDidLoad()
let x = 10
DispatchQueue.main.async { [weak self] in
print(x)
self?.someTaskWhichTakes3seconds()
print(self?.view.backgroundColor)
print(x + 2)
}
}
and after viewDidLoad was called you immediately popped the viewController off the navigation stack (or had it deallocated somehow), in that case, still print(x+2) would happen. Why? Because the block is enqueued and it has to finish.

Wait itself within endless loop, but user can cancel every time

In Swift 3 I have a loop which can be canceled by user pressing a button. Within the loop some checks are made. After the check, the task can sleep for a minute. But when calling the task with
let delayQueue = DispatchQueue(label: "com.myApp.queue3", qos: .utility)
let additionalTime: DispatchTimeInterval = .seconds(3)
repeat {
delayQueue.asyncAfter(deadline: .now() + additionalTime) { self.update() }
} while !self.stop
the loop itself needs to run all the time waiting for the user
"stop", indicates, that user clicked on stop button.
Is that waste of CPU power? How could I avoid this loop to be done all the time?
You should use Timer instead.
var timer: Timer?
let timeInterval: TimeInterval = 3
func didPressCancelButton() {
timer?.invalidate()
}
func beginUpdates() {
timer = Timer.scheduledTimer(
timeInterval: timeInterval,
target: self,
selector: #selector(self.update),
userInfo: nil,
repeats: true
);
}
func update() {
print("Updated")
}
Instead of delaying execution in thread with an outer loop you can put your loop in thread instead and make it to sleep.
import Foundation
class YourUpdatingClass {
private let updateQueue: OperationQueue
init() {
updateQueue = OperationQueue()
updateQueue.name = "com.myApp.queue3"
updateQueue.qualityOfService = .utility
}
private var updateOperation: BlockOperation?
#IBAction func startUpdating() {
guard updateOperation == nil else {
// In case if updating already started
return
}
updateOperation = BlockOperation { [weak self] in
while true {
Thread.sleep(forTimeInterval: 3)
self?.update()
}
}
updateQueue.addOperation(updateOperation!) // we just created updateOperation, so we can use `!`, but use it with caution
}
#IBAction func stopUpdating() {
updateOperation?.cancel()
updateOperation = nil
}
private func update() {
print("update") // Whatever your update does
}
}
You updating is contained in eternal while loop which takes a nap every 3 seconds.
Stopping is managed by cancelling operation, instead of checking some var in the loop.

Do I need capture self using thread class?

I have this code:
myThreadTemp = Thread(target: self, selector: #selector(threadMain), object: nil)
#objc func threadMain(data: AnyObject) {
let runloop = RunLoop.current
runloop.add(NSMachPort(), forMode: RunLoopMode.defaultRunLoopMode)
while !Thread.current.isCancelled{
//foreground
DispatchQueue.main.async {[weak self] in
self?.somemethod()
self?.somevar = 1
print("tick")
}
if Thread.current.isCancelled {
}
Thread.sleep(forTimeInterval: 1.0)
}
runloop.run(mode: RunLoopMode.defaultRunLoopMode, before: NSDate.distantFuture)
}
or I can just do this:
DispatchQueue.main.async {
self.somemethod()
self.somevar = 1
print("tick")
}
I saw this:
Shall we always use [unowned self] inside closure in Swift
But was if #objc func is used?
The 1st example looks to spin the runloop indefinitely, waiting 1s between ticks, whereas the 2nd example will execute once, on the very next run loop iteration. There is no memory management issue in terms of capturing self in the 2nd case, indeed because it is only executed once and the block is released after it (breaking the momentary retain loop that does exist between self and the block).
Assuming you are trying to tick every 1s (as I am guessing based on your questions), there is a better way to do what you are trying to do, using a timer:
// Must be executed on main thread, or other NSRunLoop enabled thread,
// or the below code will silently do nothing.
self.timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
self?.someMethod()
self?.someVar = 1
print("tick")
}
// Somewhere in the future, to stop the timer:
// self.timer.invalidate()
As you can see in the above example, with the timer case you might indeed want to refer to self with either an unowned or weak reference (as the timer block will otherwise make a strong reference to self, and self to the timer). The block should be released on invalidating the timer too, so even in this case the weak reference is not 100% necessary I guess.