I need to schedule a task in background when the application enter background state.
I have to do this in order to call a remote service each x time and then show a local notification when some event happend with the remote service.
(I know it's looks like RPN, yes it is, but for some reason I am not able to use PRM)
I tried this code :
- (void)applicationDidEnterBackground:(UIApplication *)application{
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void){
remoteServiceCallThread = [[NSThread alloc] initWithTarget:self selector:#selector(doRemoteCall:) object:nil];
[remoteServiceCallThread start];
}];
}
- (void)applicationWillEnterForeground:(UIApplication *)application{
[remoteServiceCallThread cancel];
}
I put breakpoint in the doRemoteCall selector, put is not working.
Maybe my approach is not the best one. If you have any other hack to doing this operation like I describe it I'll take it.
Thank you.
You are not starting the thread, it's initialization code is at the expiration handler block which will be called right before the app is shut down with a timeout:
A handler to be called shortly before the application’s remaining
background time reaches 0. You should use this handler to clean up and
mark the end of the background task. Failure to end the task
explicitly will result in the termination of the application. The
handler is called synchronously on the main thread, thus blocking the
application’s suspension momentarily while the application is
notified.
The task should be active for 10 minutes only (that is driven by iOS) if your app is not supporting one of the background modes (gps, audio, voip).
You also need to keep the returned UIBackgroundTaskIdentifier reference to be able to mark it as ended if the user brings the app to foreground or when task time is going to the end (that's when the handler block is called).
Related
For VOIP application we are sending PING packet to server every 10 minutes using setKeepAliveTimeout, everything works fine, however I'm not sure how to stop the handler from being called once application came foreground.
eg: Here is how I set the timeout
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
Background handler:
- (void)backgroundHandler
{
printf("10 minute time elapsed\n");
// do some action...
}
Above function is being called even after application came foreground, I have read in Apple documentation to set the handler nil to stop it. I have tried like below in applicationWillEnterForeground
[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:nil];
Still I'm getting call every 10 mins. How to handle this, Do I need to use flag only.
Any help is really appreciated.
you can do it like this
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[[UIApplication sharedApplication] clearKeepAliveTimeout];
}
You have to invoke clearKeepAliveTimeout to stop the timer. setKeepAliveTimeout: is designed to keep a voip connection on and that's why it's periodically called.
I have some test code which i am using to keep my app to stay running in the background state, which works quite well on the iOS 5.1 simulator but is not behaving that way on the actual device.
Now i already know the requirements i have to follow,
so i have set "background modes" to voip and location.
In my delegate's applicationDidEnterBackground method i am calling the following function
which i call "doBackgroundActivity( )"
to request time for application to complete some long running task in background :
-(void) threadedMethod{
while(true){
NSLog(#"looping");
[self showLocalNotification:#"This notification will come every 2 min. if the app is running in background. Close it!"];
[NSThread sleepForTimeInterval: (5)]; // 60 sec = 1 min
}
}
-(void) doBackgroundActivity{
self->_backgroundTask = [ [UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask: self->_backgroundTask];
self->_backgroundTask = UIBackgroundTaskInvalid;
}
];
[self threadedMethod];
[[UIApplication sharedApplication] endBackgroundTask:self->_backgroundTask];
self->_backgroundTask = UIBackgroundTaskInvalid;
}
As you see, the doBackgroundAcitivity() just calls the threadedMethod() and all that does is run an infinite loop which sends a local notification every 5 seconds or so.
Now in the simulator when i run this, and minimize the app, i see a notification every 5 seconds or so. The application keeps on running in the background even when i run other applications i see my app sending notifications.
But this is not happening on the device. It looks like that the OS kills the app just after the first notification is send and i dont see any more subsequent notifications which i expect to see later?
Is there something else i have to do to keep the application running in background ?
If all you are looking to do is present a notification to the user while your app is closed you should use UILocalNotifications.
You can set when the notification should fire, as well as whether or not it should repeat, and the amount of time between repeats.
As a side note, i wouldn't recommend using sleep() in a loop to control when code is executed like that because it stops the thread from doing anything at all, and is bad for any other tasks that would like to use that thread. Instead you should use NSTimers, and other ways of controlling the time at which code is executed.
Hi I have read several questions on SO about applicationWillTerminate getting called and not getting called.
I wanted to summarize what I understood as there are several posts that speak differently.
For IOS (without multitasking) it is called always when home button is pressed.
For IOS 4 and above
a. it is not called when pressing home button (as the app moves to background)
b. it is called when closing the app from the multi tasking dock and if the app has a sudden terminate flag in info.plist disabled else it is not called. ( I set the "Application should get App Died events" and even then on closing the app from the multitasking dock the terminate function did not get called)
Based on that I had a couple of questions
Is it a good practise to set the Application should get App Died events flag? ( I set the "Application should get App Died events" and even then on closing the app from the multitasking dock the terminate function did not get called)
or
Is registering for "UIApplicationWillTerminateNotification" a better thing to do than the info.plist setting?
Basically I need to do some work only when the app terminates and NOT when it moves to background.
or
EDIT (1):
When the app is terminated the following is sent to the APP. How do I catch it?
Program received signal: “SIGKILL”.
EDIT (2):
Please note : It is not getting called in IOS 4 and above when removing from the multitasking dock. You might think it is. But in my case it is not.
I am asking if anyone knows why? Is there something else I am missing.
Also Note I set the "Application should get App Died events" and even then it is not getting called.
EDIT (3):
The answer for the following question also did not work.
applicationWillTerminate does not get invoked
Anybody facing the similar issue as me?
In short, unless you have UIApplicationExitsOnSuspend in your Info.plist set to YES, in iOS4 and above there is no guarantee that applicationWillTerminate: will ever get called.
As the documentation says:
For applications that support background execution, this method is
generally not called when the user quits the application because the
application simply moves to the background in that case. However, this
method may be called in situations where the application is running in
the background (not suspended) and the system needs to terminate it
for some reason
(Emphasis mine.)
If you need to do something before the app exits you need to do it in applicationDidEnterBackground:. There is no way to catch SIGKILL.
I see -applicationWillTerminate: getting called with the following test. In a new project (I used the 'Single View Application' template), add the following to the AppDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"%s", __PRETTY_FUNCTION__);
__block UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (identifier != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:identifier];
identifier = UIBackgroundTaskInvalid;
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
for (int i=0; i < 20; i++) {
NSLog(#"%d", i);
sleep(1);
}
if (identifier != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:identifier];
identifier = UIBackgroundTaskInvalid;
}
});
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
This example will start a background task when the app enters the background. The task is just a 20s delay (with logging once a second) that keeps the app running in the background (note the difference between running in the background and suspended) long enough to allow it to be killed from the app switcher.
So, to test it, run the app, hit the home button to send the app to the background, then before the 20s delay is up, remove the app from the app switcher. After the end of the 20s, -applicationWillTerminate: is called. You can watch the console in Xcode to verify that this is the case.
I tried this in the iOS Simulator for iOS 5.1 and 6.1 (both iPhone) and saw it happen in both cases. I also tested on iPhone 4S running iOS 6.1.2 and saw the same behavior.
As I know, there are 3 situations that your application will die.
Terminated by the end user, you can do something in -[UIApplication applicationWillEnterBackground:], in which case, -[UIApplication applicationWillTerminate:] will NOT be called.
Dropped by the system, such as memory not enough, you can do something in -[UIApplication applicationWillTerminate:], in which case, we do NOT know whether applicationWillEnterBackground: has been called;
Crashed, nothing can be done except using some kind of Crash Reporting Tool. (Edited: catching SIGKILL is impossible)
Source: http://www.cocos2d-iphone.org/forum/topic/7386
I copied my state saving code from applicationWillTerminate to applicationDidEnterBackground and also added a multitaskingEnabled boolean so that I only call state saving in applicationDidEnterBackground. BECAUSE, there is one instance on a multitasking device where applicationWillTerminate is called: If the app is in the foreground and you power off the device. In that case, both applicationDidEnterBackground and applicationWillTerminate get called.
As we know that the App has only 5 sec when -applicationWillTerminate being called. So If someone want to update the server at that point. Than use
Synchronous call.
[NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:&error];
Note:- -applicationWillTerminate will not call if app is being killed from suspended state. Suspended state means app is not working anything in backgroupd. One of the solution for this is to use background task.
Based on Andrew's test, I understand the docs for applicationWillTerminate(_:) to be meant as having the following clarifications:
For apps that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the app. For apps that support background execution, this method is generally not called [right away] when the user quits the app because the app simply moves to the background in that case. However, this method may be called [instead of beginBackgroundTask(expirationHandler:)] in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason.
// absolute answer applicationWillTerminate
func applicationWillTerminate(application: UIApplication) {
print("applicatoinWillTerminate")
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
step 1: command + shift + h (double click(tab))
step 2: move app top side (kill)
step 3: applicationWillTerminate Work
is it possible not to pause the application while in background mode (when you press the home button and the app minimizes)? I have some timers and variables that i don't want to get paused.
EDIT:
I have followed this example http://evilrockhopper.com/2010/01/iphone-development-keeping-the-ui-responsive-and-a-background-thread-pattern/
I have called a timer inside however it's not getting called when i enter background mode:
- (void)applicationDidEnterBackground:(UIApplication *)application {
if(self.viewController.timerquest != NULL)
{
if(self.viewController.timerquest.timerRunning){
// Save varibales
[self performSelectorInBackground:#selector(performLongTaskInBackground) withObject:nil];
}
}
}
- (void) performLongTaskInBackground
{
// Set up a pool for the background task.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// perform some long task here, say fetching some data over the web.
//...
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES];
// Always update the components back on the main UI thread.
[self performSelectorOnMainThread:#selector(completeLongRunningTask) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void) updateTimer{
// Update my timer. This method is not being called in background mode
}
What should I do?
Thanks.
use Long Running Background tasks according to manual: http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html
Have a read here at the Apple non-technical documentation or at the technical reference.
Could you replace the timer with delayed background notifications?
Depending on what happens when your timers fire, you want to set up a local notification that fires at the same time the timer would have; this is useful when the timer would present something for the user to act on. As far as saving variables, you'll want to use -applicationDidEnterBackground: to save whatever state you need to, so that the correct variables can be loaded/generated when the app relaunches (which may not happen until the app has been exited and completely restarted again).
The types of tasks that are allowed to perform long running background tasks are pretty limited, specifically for things like GPS and playing audio. Everything else needs to decide on a task-by-task basis whether to simulate continued running (such as turning a timer to a local notification), pausing and saving necessary state to continue the next time the app is run, simply cancelling the task and gracefully restarting/notifying the user upon resuming the app, or asking for a finite length of time to finish a task (for things like finishing a download).
using sdk 4.2. I have an issue if my app is exited ( sent to background) and then immediately if user restarts it. I noticed the background task thread is still running.
This then upsets any initialising i am doing on the return to foreground thread.
How should i deal with this, is there any way to cancel the background task thread completely when returning to the foreground.
Thanks
I'm assuming you're using something similar to the following to inform the device you're executing a background process:
_myTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:myHandler];
You should also be performing this when your background task completes normally or when you cancel the task in myHandler:
[[UIApplication sharedApplication] endBackgroundTask:_myTaskID];
_myTaskID = UIBackgroundTaskInvalid;
Now, assuming that you'r re-initializing your application in your UIApplicationDelegate's applicationWillEnterForeground: or in response to a UIApplicationWillEnterForegroundNotification, you simply need to check to see if _myTaskID is set to UIBackgroundTaskInvalid to determine if your background task is still executing.
You haven't specified what kind of task you'er performing in your background task, but your application should be prepared to allow that task to complete if the application returns to the foreground while the task is still executing. Otherwise, you might want to re-consider using a background task at all.