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.
Related
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
I have the following code which makes a call to the server, before the app is about to exit. My problem is, that the code sometimes works, sometimes not. Mostly not. Here is the code:
//Set the user as in-active if app is force closed
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSLog(#"called");
bgTask = [application beginBackgroundTaskWithName:#"setInActive" expirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"disp called");
//If the app is about to exit make the user inactive
[[APIManager sharedInstance] setInActiveOnCompletion:^(BOOL finished){
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
});
}
The first NSLog gets called everytime. However the second one does not. It seems as if the app does not even go into the dispatch_async method.
EDIT: So basically all I need to do is tell the server that a user has force quit the app, while that user was signed in. How could I do this?
Any ideas?
applicationWillTerminate is the last method that get called before app is being terminated!! So, you can't do anything after that, if you want to perform something in background then begin that task when you performing your operation, you can't begin background task from applicationWillTerminate because your app will not be in background after this method call!!
When your application is in the background, it is only allowed very little amount of processing, therefore using dispatch_async will not buy you much extra time. Also, Apple does not want that.
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
I'm trying to fully understand the below code I put together after researching background tasks in iOS and am hoping for some help,
I understand the basic concept,
First we get the app singleton, then we create a block and register with the system the background task, and then finally we asynchronously dispatch the task to run.
So here are the pieces I'm looking for help with:
When background_task is assigned the block, the actual block does not have the code we want run inside it, only the cleanup code in it's completion handler, why is that?
I understand dispatch_async basically starts a new thread and starts working through the code in the block, but where in this dispatch_async request is the background_task referenced? I don't see how the system understands that the code we want executed in the dispatch_async request is related to the background_task we registered earlier.
Why do we need the cleanup code both at the end of the dispatch_async block and in the completion handler of the background_task?
Sorry if these are stupid questions, but I just don't get the syntax,
Here is the code i've cobbled together:
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance
__block UIBackgroundTaskIdentifier background_task; //Create a task object
background_task = [application beginBackgroundTaskWithExpirationHandler: ^ { //Register background_task
[application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks
background_task = UIBackgroundTaskInvalid; //Set the task to be invalid
//Above code called when endBackgroundTask is called
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Perform your tasks that your application requires
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateText) userInfo:nil repeats:YES];
NSLog(#"\n\nRunning in the background!\n\n");
[application endBackgroundTask: background_task]; //End the task so the system knows that you are done with what you need to perform
background_task = UIBackgroundTaskInvalid; //Invalidate the background_task
});
There is no relationship between the background task identifier and the work you do on a secondary thread. The background task represents a request for some extra time to run. That's all. You need to end it to tell the OS that you've completed the work you wanted to do. If you fail to do that in the time available, your app will be terminated. The task ID is just a token representing the permission from the OS to keep working for a bit of time.
You have to clean up in both places, the expiration handler and the end of your async-dispatched block, because they represent two different occurrences. In the block you dispatch to the concurrent queue, you end the task because you've completed your work in time and you want to let the OS know so it can suspend your app; it doesn't need to terminate it. In the expiration handler, that's your last chance to end the task to prevent your app from being terminated. You haven't completed your work, but you've run out of time. If you didn't end the background task at that point, the OS would kill your app.
By the way, scheduling a timer in a task running on a dispatch queue won't work. A timer is scheduled on a thread's run loop. The worker threads which service dispatch queues can be terminated at any time and, in any case, don't run their run loop.
It seem quite long time since this question was asked and answered.
I just would like to make a note here as I have a feeling that the user Woodstock (and may be many of us) a bit confused about "background thread" and "background task".
The "background thread" is considering in the context of UI thread and background thread.
The "background task" is considering in the context of the task allowable to take longer than normal duration (10 seconds in iOS).
The method beginBackgroundTaskWithExpirationHandler: tells iOS that you need more time to complete whatever you’re doing in case the app is backgrounded (please refer to app's life cycle concept). After this call, if your app is backgrounded it will still get CPU time until you call endBackgroundTask: or system signals it's going to be expired.
Just for clarity I'm setting up the background task as follows:
#interface myClass ()
#property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundUpdateTask;
#end
- (void)importantStuffToCompleteInBackground
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
[self beginBackgroundUpdateTask];
// do important background stuff
[self endBackgroundUpdateTask];
});
}
- (void)beginBackgroundUpdateTask
{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void)endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
According to beginBackgroundTaskWithExpirationHandler: docs the block will be called only shortly before the remaining background time reaches 0 in case you didn't complete the important background stuff block of code in time by calling the endBackgroundTask in the dispatch_async.
You might have all kinds of long running background tasks running in your app at any one time. There are several ways you can run a background task.
You could spawn a thread or you could use Grand Central Dispatch to push a block onto a worker queue which might look as simple as..
dispatch_async( queue, ^{
[self saveAllUserDataToServer];
} );
but don't be fooled, anything involving multithreading is difficult and dangerous. It's a big subject and it's not clear from your question if you have a specific task to do that you would like help with.
One such danger is that your app can be stopped by the user or System at any point. A background task you have already started could be half way complete and then just stop. This could be a disaster or just inconvenient.
iOS provides a way to not have these tasks halted abruptly. Anytime you start such a task you must let the system know, and then tell it when the task has finished, like so..
- (void)saveAllUserDataToServer {
UIBackgroundTaskIdentifier bt = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^ {}];
... do the work ...
[application endBackgroundTask: background_task];
}
..now you know it is safe to call -saveAllUserDataToServer on a background thread and it will run to completion even if the app is closed half way through.
The one caveat is that you only have a certain time limit to complete the task.. if you take too long the ExpirationHandler block will be called, and you MUST clean up properly in here. This is way the setup code is more likely to look like..
__block UIBackgroundTaskIdentifier bt = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^ {
.. task didn't complete within time limit.. do additional cleanup
[[UIApplication sharedApplication] endBackgroundTask: background_task];
}];
I was getting the following error when my app went to the background because of a continually running process in the background:
Can't endBackgroundTask: no background task exists with identifier 9, or it may >have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
Thankfully I came across hooleyhoop's answer and I was able to get around it with something like this code example:
func run() {
DispatchQueue(label: "ping").async {
let tid = UIApplication.shared.beginBackgroundTask(withName: "ping", expirationHandler: {})
//network code
//save data to database code
UIApplication.shared.endBackgroundTask(tid)
run() //repeat process
}
}
You can also use:
UIApplication.shared.beginBackgroundTask(expirationHandler:{})
if you don't need a nice name when debugging
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.