dispatch_after time triggers immediately - swift

This is the first time I've used GCD, I'll admit, so sorry if I've been stupid. I have a dispatch_after command which acts as a handy delay for me.
My problem is that when I send
dispatch_after(500000000000, dispatch_get_main_queue()){
println("triggered") //or any other code
}
the closure is triggered immediately (e.g. I have tested this and "triggered" prints immediately). It should take longer right? Like 500 seconds longer.
Thanks :)

The first parameter of dispatch_after(_:_:_:) is not a delay, but a point in time. From the docs:
when: The temporal milestone returned by dispatch_time or dispatch_walltime.
Discussion
This function waits until the specified time and then asynchronously
adds block to the specified queue.
You need to construct a delay relative to the current time, using dispatch_time(_:_:):
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(500 * NSEC_PER_SEC))
dispatch_after(delayTime, dispatch_get_main_queue()) { ... }

Related

How to detect (say) 2 seconds of inactivity in swift?

This question could be rephrased as: How to invoke a function if 2 seconds pass without an event (re)occurring?
I'm playing with SFSpeechRecogniser. While the user is speaking it sends continuous updates (maybe 2-3 per second). I'm trying to detect when the user stops speaking. If I don't receive any updates for (say) 2 seconds, I can assume that the user has paused speaking.
How to implement this in Swift?
I am aware that I could do:
var timer : Timer?
func f() {
on_event = { result, error in
print( "Got event, restarting timer" )
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in
print( "2s inactivity detected" )
self.timer?.invalidate()
NotificationCenter.default.post( name: inactivity_notification, object: nil )
}
}
}
But is it possible to do it without repeatedly creating and destroying the Timer instance (and thus creating a boatload of temporary Timer instances that never get used)?
One way to do it is to:
Record the current time when an event occurs
Set up a recurring timer with a granularity you are comfortable with (for example 0.25 seconds).
When the timer pops, check difference between current time and last event time. If that is greater than 2 seconds, fire your notification.
This is what I'd do if I had to recognize that a person had stopped typing for 2 seconds. Invalidating and creating timers at typing speed would be a lot of churn. You can tune this to your requirements depending on how close to exactly 2 seconds you need to be.
You could also do this by just having a timeSinceLastEvent variable, and set it to 0 when an event occurs. The recurring timer would increment this by the granularity, and check if it has reached 2 seconds and fire the notification if it had. This is cruder than doing the time math since the timer interval isn't guaranteed, but simpler.
Timer's .fireDate property is writable.
So every time a speech event occurs just do timer.fireDate = Date(timeIntervalSinceNow: 2)

SCNAction.wait not working in swift playgrounds

In my code I have a sequence in which I am telling swift to wait for a sequence to complete before i move on to another function however it seems to skip this and just move on
let earthSequence = SCNAction.sequence([SCNAction.wait(duration: 10),rollInGroup,rollOutGroup,earthNormalRotation])
defaultEarthNode.runAction(earthSequence)
nextFunction()
the result is simply moving on to the next function without waiting 10 seconds
That's because nextFunction() is called immediately after you start the action, it doesn't wait for it to be completed. runAction has a completionHandler you can use to know when the sequence has finished.
defaultEarthNode.runAction(earthSequence, completionHandler: {
nextFunction()
})

Rx Extensions - Proper way to use delay to avoid unnecessary observables from executing?

I'm trying to use delay and amb to execute a sequence of the same task separated by time.
All I want is for a download attempt to execute some time in the future only if the same task failed before in the past. Here's how I have things set up, but unlike what I'd expect, all three downloads seem to execute without delay.
Observable.amb([
Observable.catch(redditPageStream, Observable.empty()).delay(0 * 1000),
Observable.catch(redditPageStream, Observable.empty()).delay(30 * 1000),
Observable.catch(redditPageStream, Observable.empty()).delay(90 * 1000),
# Observable.throw(new Error('Failed to retrieve reddit page content')).delay(10000)
# Observable.create(
# (observer) ->
# throw new Error('Failed to retrieve reddit page content')
# )
]).defaultIfEmpty(Observable.throw(new Error('Failed to retrieve reddit page content')))
full code can be found here. src
I was hoping that the first successful observable would cancel out the ones still in delay.
Thanks for any help.
delay doesn't actually stop the execution of what ever you are doing it just delays when the events are propagated. If you want to delay execution you would need to do something like:
redditPageStream.delaySubscription(1000)
Since your source is producing immediately the above will delay the actual subscription to the underlying stream to effectively delay when it begins producing.
I would suggest though that you use one of the retry operators to handle your retry logic though rather than rolling your own through the amb operator.
redditPageStream.delaySubscription(1000).retry(3);
will give you a constant retry delay however if you want to implement the linear backoff approach you can use the retryWhen() operator instead which will let you apply whatever logic you want to the backoff.
redditPageStream.retryWhen(errors => {
return errors
//Only take 3 errors
.take(3)
//Use timer to implement a linear back off and flatten it
.flatMap((e, i) => Rx.Observable.timer(i * 30 * 1000));
});
Essentially retryWhen will create an Observable of errors, each event that makes it through is treated as a retry attempt. If you error or complete the stream then it will stop retrying.

swift - Delay a spawn function using NSTimer

I'm looking to integrate some delayed functions into my game, currently I'm working on putting a 5 second delay on calling my first function as in this time period the game goes through a countdown to get the player ready.
Currently my countdown is displayed but the game is in full swing in the background. I've got one function I'd like to delay for 5 seconds but I'm also thinking of using the same method to incorporate other objects later in the game to boost difficulty.
The call I'm looking to delay is moving.addChild(crows)
If anyone can show me how to build that would be great as I've been unable to find on site (unless I've missed it).
Don't use NSTimer in SpriteKit. Use this instead:
let delay = SKAction.waitForDuration(5.0)
someNode.runAction(delay) {
//run code here after 5 secs
}
let delay = 5.0 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
moving.addChild(crows)
}

Grand Central Dispatch problems with dispatch_release

I have a problem with a GCD solution I've made. I have a thread that runs in the background, it updates the application in a given interval. For this I use a timer.
However if the user wants to change this interval, I call
dispatch_source_cancel(timer);
Which is defined as
dispatch_source_set_cancel_handler(timer, ^{
dispatch_release(timer);
});
And then restart the the thread. When the interval is changed a second time the app crashes. Even though I do recreate the timer with a new interval.
I could avoid releasing the timer, but then I'll have memory leeks.
Any advice, what to do?
EDIT:
Timer is created like this
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0, autoRefreshQueue);
if(!timer) {
return;
}
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, refreshRate * NSEC_PER_SEC), refreshRate * NSEC_PER_SEC, refreshRate * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
//do work
});
I don't think this is the answer. dispatch_source_cancel doesn't cancel immediately, synchronously.
man dispatch_source_cancel
The dispatch_source_cancel() function asynchronously cancels the dispatch source, preventing any further invocation of its event handler block. Cancellation does not interrupt a currently executing handler block (non-preemptive).
Thus, restarting the thread might invoke the blocks concurrently if autoRefreshQueue is Global Queue.
How did you restart the thread?
EDITED:
However there are no mentions of calling dispatch_source_set_timer twice (or more) for the same Dispatch Source in the references or the manuals, dispatch_source_set_timer in libdispatch/src/source.c seems ok for it. At least, as far as my test, there are no problem.
Thus, just call dispatch_source_set_timer for a new interval.
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, refreshRate * NSEC_PER_SEC), refreshRate * NSEC_PER_SEC, refreshRate * NSEC_PER_SEC);