How to use beginBackgroundTaskWithExpirationHandler for already running task in iOS
In my app, I am uploading images to server from iPhone. While sync if user press home button, App will close.
I want app must be running in background till sync finish.
My question is: How can I add currently running task with "beginBackgroundTaskWithExpirationHandler
"?
Please share your ideas.
Despite its name, beginBackgroundTaskWithExpirationHandler:
does not actually "begin" a task. It might be better thought of as "register..." rather than "begin...." You're just telling the system that you're in the middle of doing something that would like to complete if that's ok.
Several points:
In almost all cases, you want to call
beginBackgroundTaskWithExpirationHandler:
when you start doing the thing you want to do, and thenendBackgroundTask:
when you're done. You almost never call these at the point that the user presses the Home button (unless that's the point when you start the task, such as saving something to the server).You can have as many "background tasks" as you want open at a time. They cost nothing. They're like retain counts. As long as you still have one open (a "begin" that did not get to its "end"), then the system will consider that a request for more time. There is very little cost to calling the "begin" and "end" methods, so use these to bracket anything that you want extra time to finish.
There is no way to be certain that you will get to finish your sync. These methods just make a request. The OS may still deny that request. The OS may still kill you anytime it needs some extra resources. The OS is not infinitely patient; if you take too long (about 10 minutes usually), it'll kill you anyway after calling your expiration handler. The OS may kill you because the phone is being turned off. Your app must be able to deal with not getting to finish. The OS just gives you this ability so that you can improve the user experience.
I used below code for background task handler:
__block UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"Background Time:%f",[[UIApplication sharedApplication] backgroundTimeRemaining]);
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];
backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}];
Here's how I used beginBackgroundTaskWithExpirationHandler, including the call to the method to be done in the background. This includes code for starting the new async task. So not exactly what is asked in the question. Adding code below, thinking someone might be looking for this scenario:
- (void)didReceiveObjList:(CFDataRef)message
{
// Received Object List. Processing the list can take a few seconds.
// So doing it in separate thread with expiration handler to ensure it gets some time to do the task even if the app goes to background.
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processObjList:message];
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
NSLog(@"Main Thread proceeding...");
}
Take a look at this page, Apple describe how to keep iOS application not suspended while it is in background. Apple Documentation
And here is what I've implemented:
UIBackgroundTaskIdentifier bgTask;
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(@"=== DID ENTER BACKGROUND ===");
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
// [self askToRunMoreBackgroundTask]; This code seems to be unnecessary. I'll verify it.
}];
if (bgTask == UIBackgroundTaskInvalid) {
NSLog(@"This application does not support background mode");
} else {
//if application supports background mode, we'll see this log.
NSLog(@"Application will continue to run in background");
}
}
/*
- (void)askToRunMoreBackgroundTask
{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
NSLog(@"restart background long-running task");
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
[self askToRunMoreBackgroundTask];
}];
}
*/
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(@"=== GOING BACK FROM IDLE MODE ===");
//it’s important to stop background task when we do not need it anymore
[[UIApplication sharedApplication] endBackgroundTask:UIBackgroundTaskInvalid];
}