NSTimer behavior on background/foreground - ios

Scenario is:
Application has on main run loop that runs every 5 min. When app is backgrounded, the timer is not invalidated.
On foreground after waiting a bit (say 10 min), the timer immediately fires twice. I'm unclear as to what the defined behavior here should be (if any) other than on background, the timer is paused/suspended along with the app.
It seems to me that when the app is foregrounded, the timer actually looks at the elapsed time since background, determines the number of times it should have fired in between, and fires them all. I observed this scenario happening both on iOS 7 simulator and iOS 7 iphone.
Google search didn't turn up much info on this topic. Any ideas/pointers?
Update: Clarification - my question is what is the behavior for NSTimer when the application is backgrounded and then foregrounded again, assuming the timer is not invalidated/nil-ed on background, and a new timer is not created on foreground.
Code sample (code is bit old - pre ARC days):
#implementation ReportHandler {
NSTimer *_reportTimer;
}
- (id)init
{
if (_reportTimer == nil) {
_reportTimer = [[NSTimer timerWithTimeInterval:5*60 target:self selector:#selector(didFireReportTimer:) userInfo:nil repeats:YES] retain];
[[NSRunLoop mainRunLoop] addTimer:_reportTimer forMode:NSDefaultRunLoopMode];
}
}
- (void)didFireReportTimer:(NSTimer *)timer {
// send report over network here, timer is not invalidated here
}
There are no background/foreground handlers either here or in the app delegate dealing with this timer.

It seems to me that when the app is foregrounded, the timer actually looks at the elapsed time since background, determines the number of times it should have fired in between, and fires them all. I observed this scenario happening both on iOS 7 simulator and iOS 7 iphone.
That is a correct description of the behavior of NSTimer and the run loop. When your app is suspended it won't fire (by default, when you background it; but if you start a background task, it will fire as normal while the task is running).

Related

NSTimer update interval changes when app goes to background in iOS app?

I was recording a sample of CMDeviceMotion acceleration data for 30 seconds through NSTimer. The thing is, when the app is in foreground, everything is fine. The interval is set to 0.01 and in 30 seconds, 3000 readings are stored. But when app goes to background, I get 300 only readings.
self.deviceMotionTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(recordUpdates) userInfo:nil repeats:YES];
Is this how NSTimer works?
NSTimer will never work in background. When your app goes to background, NSTimer also freezes. In short, You can't use NSTimer, when you want to do some repetitive task in background.
If you still want to run something in background, then use the answer given by #Robert in this thread:
Scheduled NSTimer when app is in background?

Watch OS2 NSTimer problems

I'm working on an App where I need to start a timer (using NSTimer) when the Watch is activated. With the Timer I asks some information to the iPhone (about every 1 seconds and maximum for 5 seconds). I'm using this to start the timer
timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(myfunction) userInfo:nil repeats:NO];
In the "myfunction" function, I restart the timer for the next time.
- (void) myfunction
{
//Here I update a label text
// [...]
[timer invalidate];
timer = nil;
counter++;
if(counter<5)
{
timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(myfunction) userInfo:nil repeats:NO];
}
}
My problem is that in the simulator all works fine but in a real watch (Watch-OS2 GM) the timer sometimes doesn't start or sometimes it starts but only for one time and after seems freeze! I see this because i Update a label in the watch at every elapsed period that shows a counter and I'm sure all is initialized in the "will activate" function. I don't understand why. Someone with the same issue?
From documentation
Use your interface controller’s init and awakeWithContext: methods to
load any required data, set the values for any interface objects, and
prepare your interface to be displayed. Do not use the willActivate to
initialize your interface controller. The willActivate method is
called shortly before your interface is displayed onscreen, so you
should use that method only to make last-minute changes. For example,
you might also use that method to start animations or start other
tasks that should only happen while your interface is onscreen.
So, what method did you use to instantiate timer?
make sure you use willActivate method and also use the didDeactivate method to clean up your interface and put it into a quiescent state. For example, use this method to invalidate timers and stop animations.
Hope this helps
My problem is solved with Watch OS 2.1. The problem was related to a quickly movement of the wrist: with the latest update of WatchOS all the timer are correctly restore after a quick move and all works fine

Running an already running NStimer in Background

In my iOS application I am using NSTimer to to record some events happening in my app. Now I want to keep them recording when my app goes to the background so that even if my app is in the background, my NSTimer will continue to record the events.
Can somebody guide me how I can achieve this behaviour?
Unless you enable one of the Background modes, it is not gonna work.
Why?
You have around 10 minutes of background execution after this the timer is stopped by ios.
The timer will not fire after app is locked (iOS7), since ios suspends the foreground app and bgTask will not get fire again.
There is some workarounds, consider to check below
// NSTimer run when app in background
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
loop = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(Update) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:loop forMode:NSRunLoopCommonModes];
Anotherway check this NSTimer in Background
Timers can not run in background. The only thing you can do is, record the time when application goes into background and when application comes into foreground. Take the difference and do the appropriate action.

NSTimer not stopping after the app goes into background

Until yesterday I was sure, the NSTimer will get stopped after the app goes into background. I'm have a feeling like experiencing some anomally.
My app has update location and play audio background modes. Update location is refreshed every few seconds. But it only happends on one of the app screens. There also is NSTimer refreshing some UI.
I've scheduled it like this:
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];
There is also a method, which content is irrelevant now:
-(void)updateTime
{
//irrelevant content, but the method gets fired even when the app is in background
}
The weird thing is, thah the method, which is only fired by the NSTimer and nowehere else is fired even after the ap go into the background. What is happening here? Is that normal behaviour?
Because you are using background modes with location and audio your app is still alive in background.and so your timers are running.
If you remove background modes with location and audio that you are using and then try the timers wont work.
Its Normal behaviour. Correct me if i'm wrong.
Create your timer as public i mean add it in .h file and access it when your app. enter in backGround Mode
- (void)applicationDidEnterBackground:(UIApplication *)application
By above method and set your timer as inValidate.. its fix.
And if you want to do again start your timer then you can access it by
- (void)applicationWillEnterForeground:(UIApplication *)application
this method. Here you need to recreate your timer.
You can stop the timer using invalidate the timer.
[self.timer invalidate];
self.timer= nil;

Is NSTimer expected to fire when app is backgrounded?

I don't understand it at all but NSTimer in my app definitely is running in background. I have a NSLog in method run by the timer and it is logging while it's in background. It's on iPhone 4 with iOS 4.2.1. I have declared location background support in Info.plist.
I read the docs and many discussions here and elsewhere and it shouldn't be possible. Is it an iOS bug? Or undocumented feature? I don't want to use it and find out in near future, for example with coming of iOS 4.3 that Apple silently "fixed" it and the app won't be working.
Does anybody know more about it?
NSTimer is going to fire whenever the main runloop is running. Apple makes no promises that I know of to unschedule timers or to prevent the main runloop from running. It's your responsibility to unschedule your timers and release resources when you move to the background. Apple isn't going to do it for you. They may, however, kill you for running when you are not supposed to or using too many seconds.
There are many holes in the system that will allow an app to run when it isn't authorized to. It would be very expensive for the OS to prevent this. But you cannot rely on it.
You can have a timer fire while in background execution mode. There are a couple of tricks:
You need to opt into background execution with beginBackgroundTaskWithExpirationHandler.
If you create the NSTimer on a background thread, you need to add it to the mainRunLoop manually.
- (void)viewDidLoad
{
// Avoid a retain cycle
__weak ViewController * weakSelf = self;
// Declare the start of a background task
// If you do not do this then the mainRunLoop will stop
// firing when the application enters the background
self.backgroundTaskIdentifier =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundIdentifier];
}];
// Make sure you end the background task when you no longer need background execution:
// [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Since we are not on the main run loop this will NOT work:
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
// This is because the |scheduledTimerWithTimeInterval| uses
// [NSRunLoop currentRunLoop] which will return a new background run loop
// which will not be currently running.
// Instead do this:
NSTimer * timer =
[NSTimer timerWithTimeInterval:0.5
target:weakSelf
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
// or use |NSRunLoopCommonModes| if you want the timer to fire while scrolling
});
}
- (void) timerDidFire:(NSTimer *)timer
{
// This method might be called when the application is in the background.
// Ensure you do not do anything that will trigger the GPU (e.g. animations)
// See: http://developer.apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW47
NSLog(#"Timer did fire");
}
Notes
Apps only get ~ 10 mins of background execution - after this the timer will stop firing.
As of iOS 7 when the device is locked it will suspend the foreground app almost instantly. The timer will not fire after an iOS 7 app is locked.

Resources