Multiple beginBackgroundTaskWithExpirationHandler - ios

If we have a long running task, we can ensure that the application doesn't quit until that task is complete by using the following piece of code:
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you.
// stopped or ending the task outright.
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Do the work associated with the task, preferably in chunks.
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
In my case, the applications plist contains an entry for Application does not run in background set to YES. Now, i have a serial queue (dispatch_queue_create(..., NULL)) that logs the operations and perform them one by one. User can post many tasks at a time and it might take couple of seconds 20-30 for all of them to complete.
Now my question is, can i use beginBackgroundTaskWithExpirationHandler with my serial queue? If so, any recommendations as to how? I believe i'll have to maintain an array of UIBackgroundTaskIdentifier?

To the best of my knowledge, if the application doesn't support background processing, the application quits immediately. So, starting a background tasks is not possible.

Related

Apple's background execution example for ios

Apple gives this of background execution:
- (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.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
This example has never made much sense to me and I've seen it copied to numerous background application examples.
The first thing that doesn't make sense are these two lines in the expirationHandler:
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
It seems like bgTask won't have a value when captured in the block. The compiler complains as such. Then below in the dispatch_async the sample shows the same two lines. I would expect it in the dispatch_async but not in the block. Can anyone explain why we have these lines in the block?
Also the documentation per beginBackgroundTaskWithName says "Marks the beginning of a new long-running background task." How exactly is it doing this? What defines the task? Is it any code that follows in the block scope?
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:]
tells iOS that your application is starting a new background task. iOS doesn't care which code comprises the task, it just knows that it needs to give your app more time to execute in the background.
After this line executes, bgTask will contain the new background task identifier.
When your background task is complete, you then call [application endBackgroundTask:bgTask]; and iOS knows that your app has finished the specified background task and may not need any more background execution time (You may still have other background tasks initiated by beginBackgroundTaskWithName:expirationHandler outstanding).
The line:
bgTask = UIBackgroundTaskInvalid;
is just housekeeping; If you omit this line nothing bad will happen, but bgTask will contain an invalid identifier.
If you don't call endBackgroundTask before your app's background time expires, then then expiration handler block will be invoked.
In the expiration handler bgTask will have the value that was assigned when you called beginBackgroundTaskWithName:expirationHandler, so this is what is passed to endBackgroundTask and again assigning UIBackgroundTaskInvalid is just housekeeping

Implement updating location to server in the background mode- iOS

What I want do is:
Updating the current location of iOS device to my server periodically(every 10min), even my App is in the background mode.
What I have done:
Add the Required Background Modes in the plist; and asked requestAlwaysAuthorization.
Set background refreshing of this App in Setting.
Set a NSTimer to get the location(using CLLocationManager) periodically.
When the locationManager: didUpdateLocations:delegate called, updating location use HTTPGET to the server.
My problem:
When App is in foreground it works fine, but the update location method does not run in background, server cannot receive my location data.
Thank you.
Please check if you have enable Location in background modes from project settings -> Target -> Capabilities.
Instead of location send to server in every 10 mins , I would like to suggest you use startMonitoringVisits() and locationManager(_:didVisit:) delegate to get user location. Because your approach will be too much battery killer.
You have to register your background task with below code and then your task is finish you have to end background 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.
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;
});
}
for more info: apple document

BLE background operation issue

I am trying to transfer a firmware file from my app to a wearable hardware.
it takes about some time and when my app goes in background or the lock button is pressed the firmware transfer process discontinues.
ideally it should continue to transfer the firmware. I am using this method to continue the process in background and also also have declared the support for the background modes.
- (void)applicationDidEnterBackground:(UIApplication *)application {
bgTask = 0;
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.
//[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
This method is not able to do the task.
However if I use this method in Appdelegate - didFinishLaunchingWithOptions it works.
But there is a trade off with putting this piece of code in the above method i.e If I am not transferring the firmware and the app goes in background then after 3 mins this piece of code removes the app and bluetooth connection breaks.
If I am not using this method at all, then the connection remains until it is broken manually but background transfer does not happen.
I have to keep both the operations simultaneously. Please suggest something as I have been for many days on this particular problem.
Thanks in advance.
Try to run your application using BackgroundModes
and remove all the codes from didEnterInbackground

ios background tasks - explain sequence of execution?

Could someone explain sequence of execution please in applicationDidEnterBackground?
UIBackgroundTaskIdentifier background_task;
background_task = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"\n\nRunning in the background!\n\n");
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
});
My understanding is
create identifier for background task and assign block that will be called once time (10 minutes or so) expires
dispatch async method, output NSLog. During this time all other methods of the app can be used
immediately after NSLog out terminate background task, not waiting for system default expiration
Specifically, after I call NSLog
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
task will be terminated and expirationHandler block will not be called.
I also think my understanding is incorrect...
Everything about your post is basically correct except for one important detail. None of this has anything to do with the applicationDidEnterBackground app delegate method.
Any task in your app that might take more than a couple of seconds should be wrapped inside calls to beginBackgroundTaskWithExpirationHandler and endBackgroundTask.
The whole point of wrapping code in these two methods is to notify the OS that you have some processing that needs to keep running even if the app happens to enter the background while it is running. Without these blocks, your app will be killed by the OS after only a few (10?) seconds of trying to run in the background.

Continue operation when app did enter background on iOS

in my app i have some NSOperation that update some core data element from a online database, sometime the update require some minute, and when the screen of iPhone lock, the app enter in the background mode, and this update is stopped, so i have to reopen the app to continue the update, so i have search a lot on stack overflow and i have find some information about:
beginBackgroundTaskWithExpirationHandler
that is a method from apple that let continue some task also when the app is in the background mode, and i have do this:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
and now the app continue the task in the background, and seems that all works fine, so my question is, this method i use is safe? or there is a better mode?
thanks
That's not how you do this. Any code that you want to run in the background must be wrapped properly. Something like this:
- (void)someMethodToKeepRunningInBackground {
UIBackgroundTaskIdentifier taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) {
// Uh-oh - we took too long. Stop task.
}];
// Perform task here
if (taskId != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:taskId];
}
}
You don't do anything in the UIApplicationDelegate applicationDidEnterBackground: method.
Any task that is wrapped inside the "background task" calls will be allowed to keep running when the app enters the background.
Here's the really important part - the task only gets 10 minutes maximum. If it is still running after 10 minutes your app will be terminated. The expiration handler gives you a few seconds to cleanly end the task before the app is terminated uncleanly.

Resources