I'am working on an application in xcode-5, I need to upload some data in background mode, I need a set of code to be repeated till the device terminates the background process. I am able to do it once but I need to repeat again and again so that i can check the connectivity and perform the upload. I doing the same thing when the application comes in active mode.
Currently I'm using Timers with the following code :
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[bgrdTimer_ActiveMode invalidate];
bgrdTimer_ActiveMode=nil;
self.backgroundTask=[application beginBackgroundTaskWithExpirationHandler:^{
self.backgroundTask=UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
bgrdTimer_BackGroundMode=[NSTimer scheduledTimerWithTimeInterval:40.00 target:self selector:#selector(repeatUploadProcess_BackGroundMode) userInfo:nil repeats:YES];
});
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[bgrdTimer_BackGroundMode invalidate];
bgrdTimer_BackGroundMode=nil;
bgrdTimer_ActiveMode=[NSTimer scheduledTimerWithTimeInterval:40.00 target:self selector:#selector(repeatUploadProcess_ActiveMode) userInfo:nil repeats:YES];
}
repeatUploadProcess_ActiveMode and repeatUploadProcess_BackGroundMode are the methods containing the set of code which is suppose to be repeated
the problem is when I invalidate the bgrdTimer_ActiveMode the other timer dosn't gets invoked.
Have you enabled the background fetch mode? Click on the project target, and under the capabilities tab select "background fetch".
Theoretically you should also be setting [application endBackgroundTask:bgTask]. It sounds like you know, but if it never ends, iOS will end up killing your app.
Related
As far i know, there are two distinct stages of iOS application lifecycle when it is hidden:
Background - when we are still able to proceed computation and run code;
Suspended - when any computation is freezed and we can't do anything till the user returns to the app.
So i'm a bit confused about a following example:
- (void)applicationDidEnterBackground:(UIApplication *)application {
(void)(^expirationHandler)() = [^{
[application endBackgroundTask:bgTask];
self.bgTask = UIBackgroundTaskInvalid;
} copy];
self.bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:expirationHandler];
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(count)
userInfo:nil
repeats:YES];
expirationHandler();
}
- (void)count {
int i = 0;
while (i < 10000000) {
i++;
}
NSLog(#"%d", i);
}
P.S. I've reproduced it from memory, let me know if something wrong, but question is not related to correctness of the snippet.
I tested it on iPhone 6+ with iOS 9.3 and the specified timer persists firing, furthermore count method executes code (you can check it by i value). Am i right that app must proceed to suspended state after the timer scheduled? If so, why is count able to execute code?
Thanks in advance.
As per #Paulw11 in comments,
Are you testing while running under the debugger in Xcode? If so, then you app doesn't have background time limits or get suspended
I haven't found any proofs, but through empirical enquiry it appears to be true.
I'm working on an sdk that uses NSTimer to make a server call when it expires at x mins and the server call resets the timer back to x mins after doing some work. The problem I'm having is that my timer stops running when my app is in the background and resumes immediately when my app is back in the foreground. How do I get this to work?
//initialize self.myTimer somewhere in my code
//called once somewhere in my code-->[self resetTimer:self.myTimer expiry:30];
- (void)resetTimer:(NSTimer *)timer expiry:(float)seconds {
[timer invalidate];
NSTimer *newTimer = [NSTimer timerWithTimeInterval:seconds
target:self
selector:#selector(updateTimer:)
userInfo:nil
repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:newTimer forMode:NSRunLoopCommonModes];
self.myTimer = newTimer;
}
- (void) updateTimer:(NSTimer *)timer {
[timer invalidate];
dispatch_queue_t queue = dispatch_queue_create("update", NULL);
dispatch_async(queue, ^{
[self serverCall];
});
}
-(void)serverCall{
//make server call and do some work
[self resetTimer:self.myTimer expiry:30];
}
The timer won't continue to fire when the app is in the background. If you're willing to let the OS set the pace of the server calls, you can accomplish this by consulting the section "Fetching Small Amounts of Content Opportunistically" in this background execution doc.
The gist is to set your app's UIBackgroundModes key == fetch in info.plist.
When iOS decides to grant your app some cycles, you can run a short task in:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[self serverCall]; // BUT! read on
}
But you'll need to refactor your serverCall to tell the caller when its done. Otherwise, you won't know when to invoke the completionHandler:.
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[self newServerCallWithCompletion:^(BOOL gotNewData) {
UIBackgroundFetchResult result = (gotNewData)? UIBackgroundFetchResultNewData : UIBackgroundFetchResultNoData;
completionHandler(result);
}];
}
See the docs for more options on UIBackgroundFetchResult.
With this, iOS will set the pace of requests, so you won't be able to make them more often than when you're app is given the chance. By persisting the time of the last request, you can make requests less often. Just check if the interval since the last request is <= to some desired max frequency. If it is, just call the completionHandler right away with "no data".
I have in my app login and logout process here I am facing so many problem with background timer services process. Below code I am maintaining for notification and JSON service background while applicationDidBecomeActive.
-(void)applicationDidBecomeActive:(UIApplication *)application
{
checkJSONtimer = [NSTimer scheduledTimerWithTimeInterval:300 target:self selector:#selector(updateJSON) userInfo:nil repeats:TRUE];
NSLog(#"%s", __PRETTY_FUNCTION__);
application.applicationIconBadgeNumber = 0;
}
Problems below
Before login, timer activity started
After logout, timer activity not stopping
If I commented above timer after background to come active state,
then timer not working
Every time without logout , cleared background app running again
open I can go from login
FYI : Above timer selector method I am maintaining below three places
1. application didFinishLaunchingWithOptions
2. applicationDidBecomeActive
3. applicationDidEnterBackground
How to solve above problems. Please help me!
Before login, timer activity started
For that you have to make a check whether the user is log-in the app or not.For example
-(void)applicationDidBecomeActive:(UIApplication *)application{
if(isUserLogedIn) {
checkJSONtimer = [NSTimer scheduledTimerWithTimeInterval:300 target:self selector:#selector(updateJSON) userInfo:nil repeats:TRUE];
NSLog(#"%s", __PRETTY_FUNCTION__);
application.applicationIconBadgeNumber = 0;
}
}
After logout, timer activity not stopping
For that, make a Notification which will trigger when the user log-out from the app.When the notification triggers the invalidate the timer.[myTimer inValidate].
If I commented above timer after background to come active state, then timer not working
No need to comment the timer.Just manage the timer affectively.
Every time without logout , cleared background app running again open I can go from login.
For that you have to manage the User session in NSUserDefaults.Check the value, if the value is not null, the log-in user automatically.
If you want to cancel the timer you can use something like this:
AppDelegate* appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[appDelegate.checkJSONtimer invalidate]
I am trying to implement certain app feature when app goes to background. But i saw a strange issue that if i do NSLog after every 2 seconds using NSTimer in "applicationDidEnterBackground", using simulator it works, but when i tested it in actual device, it doesn't print. Below is my code from "AppDelegate.m":
- (void)printLog
{
NSLog(#"logging after 2 sec.");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bckTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(printLog) userInfo:nil repeats:YES];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[bckTimer invalidate];
}
Please let me know why it's happening like this and may be any tips you would like to share while working with background app feature execution.
Thanks
I'm developing a Cordova project using this plugin: Cordova Plugin Background Geolocation
to get GPS updates when my app is in background.
I would like to stop getting updates after 3 minutes the app is on background.
I don't have the Objective-C skills to modify the plugin to achieve this.
I guess that there's a way to use a timer on Objective-C to stop the service after 3 minutes.
Can anyone help me with this?
UPDATE
The plugin has a stop method here.
Try this solution, here you need to create global object of NSTimer and check whether NSTimer object is validate or not
-(void)applicationDidEnterBackground:(UIApplication *)application {
if ([objTimer isValid]) {
[objTimer invalidate];
}else{
objTimer = [NSTimer scheduledTimerWithTimeInterval:180 target:self selector:#selector(stopGPS:) userInfo:nil repeats:NO];
}
objTimer = nil;}
- (void)stopGPS:(NSTimer *)timer {
// code for stoping GPS service.}
You must modify your .plist file to indicate that you will be running tasks once the app has entered background...That being said, what you want might look something like this (all in AppDelegate)
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Entered background");
[self monitorLocation];
}
- (void)monitorLocation
{
[NSTimer scheduledTimerWithTimeInterval:180 //That's in seconds
target:self
selector:#selector(stopMonitoring)
userInfo:nil
repeats:NO];
//Do whatever monitoring here
}
- (void)stopMonitoring
{
//Stop monitoring location
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(#"app will enter foreground");
[self stopMonitoring];
}