UIBackgroundTaskIdentifier is not working relaunching after its expires - ios

I have a app that fetch some content from server via REST api after every 5 mins in background using the UIBackgroundTaskIdentifier. My problem is that this works fine for 1,2 hours and then after it is expired it never re starts the background task. The code I am using is given below,
In AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIApplication* app = [UIApplication sharedApplication];
self.expirationHandler = ^{
[app endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
// self.bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
NSLog(#"Expired");
self.jobExpired = YES;
while(self.jobExpired) {
// spin while we wait for the task to actually end.
[NSThread sleepForTimeInterval:1];
}
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
// Restart the background task so we can run forever.
[self startBackgroundTask];
};
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
[self monitorBatteryStateInBackground];
}
- (void)monitorBatteryStateInBackground
{
NSLog(#"Monitoring update");
self.background = YES;
[self startBackgroundTask];
}
- (void)startBackgroundTask
{
NSLog(#"Restarting task");
// Start the long-running task.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// When the job expires it still keeps running since we never exited it. Thus have the expiration handler
// set a flag that the job expired and use that to exit the while loop and end the task.
while(self.background && !self.jobExpired)
{
[self uploadPhotostoServer];
[NSThread sleepForTimeInterval:240.0];
}
self.jobExpired = NO;
});
}
In expired section it do come but never calls the method [self startBackgroundTask]
Any help will be much appreciated.

Related

Can I use beginBackgroundTaskWithName for normal app processing

I have some NSOperations that are started regularly in my application. They should complete even when the the application is put to background. For this, I'm using the beginBackgroundTaskWithExpirationHandler method.
Am I supposed to use the beginBackgroundTaskWithExpirationHandler/ endBackgroundTask: every time I start my task even if the app is not going to background? Or am I supposed the call the begin/end methods only when I detected a UIApplicationDidEnterBackgroundNotification?
Option 1: Use background task every time
/**
* This method is called regularly from a NSTimer
*/
- (void)processData
{
__block UIBackgroundTaskIdentifier operationBackgroundId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
operationBackgroundId = UIBackgroundTaskInvalid;
}];
NSOperation *operation = ...
[self.queue addOperation:operation];
operation.completionBlock = ^{
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
operationBackgroundId = UIBackgroundTaskInvalid;
};
}
Option 2: Use background task only when the application is about to go to background
/**
* This method is called regularly from a NSTimer
*/
- (void)processData
{
NSOperation *operation = ...
[self.queue addOperation:operation];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
__block UIBackgroundTaskIdentifier operationBackgroundId = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"EnterBackgroundFlushTask" expirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
operationBackgroundId = UIBackgroundTaskInvalid;
}];
// wait for all operations to complete and then
// let UIApplication know that we are done
[[UIApplication sharedApplication] endBackgroundTask:operationBackgroundId];
}
Answering my own question. From the Apple Docs:
You do not need to wait until your app moves to the background to
designate background tasks. A more useful design is to call the
beginBackgroundTaskWithName:expirationHandler: or
beginBackgroundTaskWithExpirationHandler: method before starting a
task and call the endBackgroundTask: method as soon as you finish. You
can even follow this pattern while your app is executing in the
foreground.
Other Apple API reference:
You should call this method at times where leaving a task unfinished might be detrimental to your app’s user experience.
You can call this method at any point in your app’s execution.
Option2 is correct option.Here is code from Apple document for your reference.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[self processData];
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
Apple developer Guide

iOS limit background time

I need to run some code when the user enters a background state. The default time I was getting for when I entered the background on iOS 9 was 10 seconds. I needed a bit more than that, so I found that this code will extend the time to 3 minutes:
- (void)extendBackgroundRunningTime {
if (_backgroundTask != UIBackgroundTaskInvalid) {
// if we are in here, that means the background task is already running.
// don't restart it.
return;
}
NSLog(#"Attempting to extend background running time");
__block Boolean self_terminate = YES;
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"DummyTask" expirationHandler:^{
NSLog(#"Background task expired by iOS");
if (self_terminate) {
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
_backgroundTask = UIBackgroundTaskInvalid;
}
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Background task started");
while (true) {
NSLog(#"background time remaining: %8.2f", [UIApplication sharedApplication].backgroundTimeRemaining);
[NSThread sleepForTimeInterval:1];
}
});
}
However, my task doesn't need all of this extra time, and I would like to conserve as much battery as possible. Is there any way to use this or similar code to get 1 minute of background time, or some other value between 10 and 180 seconds?
You should call endBackgroundTask: once you are done with your background processing. If you are done before the 3 minute time allotted to you, that should end your background processing early and let iOS suspend you. I haven't tested it to verify, but that's what the docs suggest.

Fetching data using NsoperationQueue in sleep mode

I have added data fetching operations to NSOperationQueue .During the process, I just put the device to sleep mode and the process gets stopped. I surfed stack overflow an get some basic ideas.I need help to continue my fetching process without any interruption when device moves to sleep mode.Help appreciated !!
NSManagedObjectContext *managedObjectContext=((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
NSOperationQueue *downLoadQueue=((AppDelegate *)[[UIApplication sharedApplication] delegate]).downloadqueue;
//Fetch operation
if([fetchqueue count]>0)
{
Queue *queue=[fetchqueue objectAtIndex:0];
queue.status=#"INP";
[managedObjectContext performBlockAndWait:^{
NSError * error = nil;
if (![managedObjectContext save:&error]){
NSLog(#"Unresolved error while loading3");
}
}];
DownloadOperation *downloadOp=[[DownloadOperation alloc]init];
downloadOp.queue=queue;
[downLoadQueue addOperation:downloadOp];
}
}
The downLoadQueue started its execution. It is fetching data from server meanwhile the device goes to sleep and the execution stops. I don't know how to continue in this block applicationDidEnterBackground. How can I get my lengthy downloading process during sleep mode?
Also tried this one..But the operation was not resumed.
//code follows
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSOperationQueue *downLoadQueue;
downLoadQueue=((AppDelegate *)[[UIApplication sharedApplication] delegate]).downloadqueue;
[downLoadQueue waitUntilAllOperationsAreFinished];
});
}
Please suggest any ideas.
I think the only way to do this is background task. Take a look at apple docs about this. There are also a lot of answers on stackowerflow how to implemet such feature.
I think in your case you can try next code to execute NSOperations a in backgraund task:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
//Start your NSOperationQueue if it's not executing
//Lock current thread while operations are executing
[queue waitUntilAllOperationsAreFinished];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}

AsyncSocket crash on sleep mode for iPad

I am using robbiehanson/CocoaAsyncSocket for async socket, when my iPad goes into background state i disconnect and close all the ports, but some times when iPad awakes from sleep it crashes, happened same with simulator when mac goes to sleep mode. Once i got crash log as [AsyncSocket close] unrecognized instance send to selector.
I am not able to find a solution for this, can any one help me on this. Below is my code for disconnecting.
- (void) enterBackground
{
if (self.discoveryUdpSocket!=nil)
{
self.discoveryUdpSocket.delegate = nil;
[self.discoveryUdpSocket close];
}
self.discoveryUdpSocket.delegate = nil;
self.discoveryUdpSocket = nil;
}
Have you tried to extend time when entering background mode to allow socket framework complete its job?
I mean to add in the app delegate a method:
-(void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"Times up!");
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Let the async socket to complete its job and finally close the connection
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Finishing job and closing async socket, time remaining=%f", [app backgroundTimeRemaining]);
// Calling your background routine
[self enterBackground];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
This is a good way to complete important job just before app is suspended. You have max 180 secs.

Continuing a long running process in the background under iOS4

I have have an upload queue running using ASIHTTP request. When the user presses the home screen and the app goes into the background I would like this operation to continue. From the documentation I can see how I would call a new task could be called to run in the back ground, but can't quite see how I would flag an already running task to continue.
this example is bracketed for pre-4.0 compatibility:
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(beginBackgroundTaskWithExpirationHandler:)]) {
backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
// you took too long - clean up what you can, then …
[app endBackgroundTask:backgroundTaskIdentifier];
backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}];
}
// start HTTP request …
when you complete your process, you should call endBackgroundTask: to let the app know

Resources