So I did some more testing. Used two types of timers:
Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(doSomeStuff), userInfo: nil, repeats: true)
and
queue = DispatchQueue(label: "Timer", qos: .background)
timerSource = DispatchSource.makeTimerSource(queue: queue)
timerSource.schedule(deadline: .now() + .seconds(Int(nextRunTime.timeIntervalSinceNow)), repeating: 2 * 60, leeway: .seconds(10))
timerSource.setEventHandler { [weak self] in
self?.doSomeOtherStuff()
}
timerSource.resume()
This is all happening inside NetworkExtension
with VPN connected at the time of testing, so the process runs all the time, except when it doesn't :)
Following are timestamps from syslog. I selected two of them (first one and the last) that are from the first Timer
. Others are just logs from VPN library. As you can see in between there were some things going on, NE was woken up (unfreezed? don't know how to call that state) several times and traffic was flowing, extension was running then got to void again, then running, etc. Still the timer was not called.
The second timer was set to be fired each two minutes, so it was called even less frequently than the first one.
All in all it looks like timer does not respect the wall time and just uses some internal counter that ticks according to its runloop. And if between two runs OS decides to freeze the process, this time just disappears from the counter.
- Is there any timer or a mechanism like that that can call my code every now and then and in case the process was frozen in between would count time according to real time?
- Where can I find any documentation how this freezing/unfreezing works on iOS/ipadOS?
- Is there a way to detect that NE was frozen for some time and just got some time to run again?