I wanted to add a delay to a swift 3 program and found good examples here at SO using DispatchQueue.main.asyncAfter(). I tested it in Playground and it it does add a delay. What baffles me though is that adding a delay of 61 (secs.) apparently spends 67 secs.
let date1: Date = Date.init()
func print_delay(s: String) -> Void {
print(s)
}
func delay(d: Double) -> Void {
DispatchQueue.main.asyncAfter(deadline: .now() + d) {
let date2: Date = Date.init()
let calendar: Calendar = Calendar.current
let components: DateComponents = calendar.dateComponents([.year, .month, .day, .hour, .second], from: date1, to: date2)
print_delay(s: "delta: \(components.second!)")
}
}
let delay_array = [1.0, 5.9, 10.2, 22.1, 31.5, 40.9, 51.8, 61.0]
for item in delay_array {
delay(d: item)
}
delta: 1
delta: 5
delta: 10
delta: 22
delta: 34
delta: 42
delta: 56
delta: 67
So I tested the same code in a command line program to see if it was more accurate but it also have the difference in the time. This is on macos sierra, latest xcode and on a macbook pro from 2012.
let key = readLine()!
blocked the main thread until you do some input. .asyncAfter
will dispatch your code in the same thread, but the code is not able to run until readLine
finished.
UPDATE
try in the Playground
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
import Dispatch
import Foundation
let t = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)
t.scheduleRepeating(deadline: .now(), interval: 3.0)
var i = 10
t.setEventHandler {
print(Date())
i -= 1
if i < 0 {
PlaygroundPage.current.finishExecution()
}
}
t.resume()
something much closer to you needs :-)
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
import Dispatch
import Foundation
let delay_array = [1.0, 5.9, 10.2, 22.1, 31.5, 40.9, 51.8, 61.0]
var tarr:[DispatchSourceTimer] = []
let start = Date()
let dt: DispatchTime = .now()
for delay in delay_array {
let t = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)
t.scheduleOneshot(deadline: dt + delay)
t.setEventHandler {
print(start.timeIntervalSinceNow)
}
t.resume()
tarr.append(t)
}
There is not necessary to use an array of dispatch sources, you can reuse the source ... it is up to you. Look at the precision :-), it is great, isn't it?
reusing the same source, you can write something like
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
import Dispatch
import Foundation
let delay_array = [1.0, 5.9, 10.2, 22.1, 31.5, 40.9, 51.8, 61.0]
let start = Date()
let dt: DispatchTime = .now()
let t = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)
var i = 0
t.scheduleOneshot(deadline: dt + delay_array[i])
t.setEventHandler {
print(start.timeIntervalSinceNow)
t.suspend()
i += 1
if i < delay_array.count {
t.scheduleOneshot(deadline: dt + delay_array[i])
t.resume()
} else {
t.cancel()
PlaygroundPage.current.finishExecution()
}
}
t.resume()