Why timer invoke its block so quickly sometimes - ios

I create a timer and invoke its block every 5 second. Then I make application to enter background and enter foreground after a while. But it could invoke the block quickly sometimes.
let _ = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { (timer) in
print("--------")
}
When I enter foreground the interval of first printing and second printing may less than a second sometimes. Is time interval invalid in this case?

To understand the behavior, you need to understand how NSTimer and RunLoop works. In simple terms, a RunLoop would check if the Timer should fire, if yes it would notify the Timer to fire the selector, else it won't. Now, since you are on the background, your RunLoop is not checking for events so it won't be able to notify the Timer. But once it goes to foreground, it would see that it would need to notify the Timer even if it passed the fireDate.
TimeLine Diagram:
Let A(5th second) and B(10th second) be timer fire events. Scheduled on a timer Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true)
C is entered background (0 seconds)
D be coming back to foreground (9th second, between A and B).
-----> A ------> B
C--------->D
Explanation:
On C, the RunLoop would be paused. Therefore Event A is not able to be processed until the RunLoop has resumed processing, which is on Event D. Upon Event D, it will see that Event A should fire so it would notify the Timer. After a second, the RunLoop would see that Event B has happened so it would notify the Timer again. This scenario explains why your events are printing in a second's interval. It is just the delayed event handling that makes it seem that it fired earlier, when in reality it was processed late.
Apple Doc:
A timer is not a real-time mechanism. If a timer’s firing time occurs
during a long run loop callout or while the run loop is in a mode that
isn't monitoring the timer, the timer doesn't fire until the next time
the run loop checks the timer. Therefore, the actual time at which a
timer fires can be significantly later.
resources: What is an NSTimer's behavior when the app is backgrounded?, NSRunLoop and Timer Docs
Suggestions:
Stop your timer once app goes background, but store the fireDate. Once coming back to foreground, check if fireDate is past Date(). Then create a new Timer to handle events while on foreground.

When application entered background, the app would be suspended soon, program stopped running. When app switched back to foreground, the buffered/delayed timer event would be fired, then you saw many prints quickly.

Related

In a iOS app, if a timer is scheduled to fire and the app is in background, am I certain that it will fire as soon as the app goes foreground again?

I a swift iOS app, I have a timer that fires every day (86400 seconds). I have tested the behaviour of the timer when the app is in background, and it seems to me that in that situation, the timer will fire only when the app goes into foreground again. Am I guaranteed of this behaviour ? I want to be certain the timer does fire. It is fine for me if it fires when the app goes in foreground again, as long as it fires then. Thanks
The short answer is that an application can continue "executing" in the background indefinitely for only a limited number of reasons. Aside from these specific reasons, the app can ask for short periods of time to continue executing in the background. Once these periods are over you tell iOS you are done, in which case the app is suspended, or iOS will eventually forcibly terminate your app (it depends on resources).
So, firing a timer is not one of those specific reasons. However, the processing you are doing with the timer could be!
Your app will briefly be in the background on its way to being suspended. When it is suspended, it is not executing anything -- including the timer.
If your app is terminated (swiped from memory or shutdown by iOS), it isn't coming to the foreground, it is being launched again. And your timer is firing because you are launching your app.
If your app stays suspended, it will come to the foreground. And your timer will fire because of the time interval involved.
Either way you can guarantee that your timer fires.

How to keep a timer counting when app reach background

I prepared a CountDown timer for Pomodoro technique. I would like to know how don't pause the app when it reach a background. I have a method which update UILabel from 20min to 0 by 1sec. When Timer reach 0 it should play the sound and vibrate device. All works fine when app is launched in foreground, but how to do it at background? Is it possible to track timer change when app is in background mode?
BR
iMat
The short answer is no. A timer on a VC will not continue to run when the app is in the background because it goes into suspended mode.
You could schedule a local notification to fire when the app is in the background, but as far as updating the UI label, you'll have to update that when the user comes back into the app.
Invalidate the timer when the app goes to background. Store the remaining time remainingTime and current time backgroundTime. (You can get the current time using Date())
Compare the current time backToForegroundTime when the app comes back with backgroundTime. Subtract them to get the time elapsed timeElapsed.
If timeElapsed is less than the remainingTime, subtract that amount from remainingTime and create the timer again with the new duration.
You can use my approach from this gist. Just create repeating timer and update what ever you want in repeating block, and handle timer finishing in other block on main queue or background queue!
Glad to help with questions!
Apple has defined a specific set of tasks, an app can perform when in background.
Running a timer, unfortunately, is not one of them.
Read Background Execution section of app programming guide for more details.
Most apps, intending to continue to execute code in background, implement one of the allowed long running background modes, even if it is not required for your apps actual functionality, and use them to execute their code.
But be ware, you will be doing something apple specifically asks you not to do. This could result in app store rejection if found.

Application's run loop slows down when receive/initiate a cellular call

I have a timer that fires a method in every 60ms of interval when the application is in foreground and fires in the same interval even if it is in background.
When I initiate/receive a cellular call, the timer fires the method in every 120ms of interval.I thought it is a problem with the timer, so I tried the following approaches.
Approaches I have tried:
NSTimer in background thread.
NSTimer in main thread.
dispatch_source_timer
while loop with 60ms of sleep. (No timer here)
So even if you use a simple while loop, still there is a delay in firing the method. So to maintain the interval I changed the timer interval to 30ms(for all the approaches) when I receive/initiate a call but the result is same(120 ms).
I will be glad if anyone can suggest an approach.
From the Apple Doc:
If a timer’s firing time occurs during a long callout or while the run loop is in a mode that is not monitoring the timer, the timer does not fire until the next time the run loop checks the timer. Therefore, the actual time at which the timer fires potentially can be a significant period of time after the scheduled firing time
What you should remember about Timer is that when you set it to a time T, you have the assurance that the elapsed time between two ticks is at least of T.

How to set a timer to run when an app is both in foreground and in background?

I've my app set to keep receiving location updates while in background ("Background modes > Location updates"). My app listens for location updates while in foreground as well. But I need my app to keep listening for locations during a certain number of seconds I'm given, and to show a countdown if the app is in foreground, and to keep the time count if the app goes to background because I need to stop the location manager when the time ends.
I found no problems in setting an NSTimer when the app is in foreground, and showing a countdown, but I don't know how to handle it when app goes to background... in comments in AppDelegate's delegate applicationWillResignActive: and applicationDidEnterBackground: methods it is said that timers should be disabled and invalidated there... in addition, the timer I'm using is created in main thread:
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateCountdown:)
userInfo:nil
repeats:YES];
How could/should I manage this scenario?
Thanks in advance
EDIT: I'm going to try to explain an scenario I could have: my app listens for locations during a given time interval, and then it should stop listening. Let's say I'm going to give the user 5 min to reach certain location. User taps a kind of "start" button, and then I start the location manager to keep track of the user's positions, and I also start a countdown. When the countdown reaches 0, I stop the location manager and check the path the user has followed and the location he has reached. Since the app could go to background after the user has started the countdown, I have enabled Background modes > Location updates. Listening for locations while the app is in background is ok but, what if the countdown finishes while the app is still in background? I need to stop tracking user's positions even if the app is in background. I need a timer to keep running in background whatever countdown I need to set for the user, it could be 5 min, it could be 1 hour...
Thanks again
You can't run a timer in the background forever, it's not one of the background modes allowed by the OS so it will get terminated at some point. Wrapping it in a background task will only buy you a few extra minutes (about 10) at the most.
To properly track time when your app enters the background you need to:
From applicationDidEnterBackground
Save off a time stamp as soon as your app transitions to the background.
Terminate your timers
When your app re-enters the foreground:
From applicationWillEnterForeground edit or 'didUdateLocations'
Read the save time stamp
Use timeIntervalSinceDate to get the lapsed time in seconds
Restart your timer using the time span to set the remaining time
I think this kind of control needs a middleware to operate perfectly. I don't know if your app has backend or not but if it has, you may send a request when user tries to start timer in your app and you do your updates background of your app. After timer finishes backed can send a notification to user about this and you handle it if user comes back to your app or you leave it that way.

iOS UIWebView Javascript Timers

If a page loaded into a UIWebView contains a Javascript setInterval() call, what is the behavior of that timer when the phone goes sleep?
Is there any point in time when all timers are stopped?
Will the timers be restarted when the phone is woken up? If so, does the timer start at where it was paused, or started from 0?
WebKit internal timer management is a little peculiar in general, and in particular in iOS. They are registered in NSDefaultRunLoopMode mode, so for example, when scrolling in UI, Javascript events do not fire, but they are not aggregated either, like with regular timers (NSTimer).
To answer your question, when the application process is suspended, the timers are also suspended, so they will not fire while the process remains suspended. Once the process is resumed for whatever reason (user opened the app, background fetch, etc.), the timers will resume their run, and will resume to the relative point of when the last tick was supposed to be.
So if you set a timer for every 10 seconds, and close the app at t+1 seconds, then open it again at t+35 seconds, the timer will fire after 5 seconds; you would not hear retroactively the t+10, t+20 and t+30 seconds ticks.
Note: This is the in-process model based timer management of UIWebView /WebKitLegacy/; I am not quite experienced with how WKWebView /WebKit2/ handles them.

Resources