My iOS app doesn't run in background. When home button is hit the app will terminate.
Below is my code
- (void)applicationDidEnterBackground:(UIApplication *)application
{
double sleepTime = [[UIApplication sharedApplication] backgroundTimeRemaining] - 1;
[self performTaskforTime:sleepTime];
}
If the application is relaunched in no time, the New instance of the app will killed with the error :
Exception Type: 00000020 Exception Codes: 0x8badf00d Highlighted
Thread: 0
Application Specific Information: My_App failed to launch in time
I can't reduce the sleep time. Is there any solution for this?
You need to return from - (void)applicationDidEnterBackground:(UIApplication *)application, do something like this:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIBackgroundTaskIdentifier btid = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
double sleepTime = [[UIApplication sharedApplication] backgroundTimeRemaining] - 1;
dispatch_async(/* background queue */, ^{
[self performTaskforTime:sleepTime];
[[UIApplication sharedApplication] endBackgroundTask:btid];
}
}
You don't want to block the main queue with a long running task as you won't be able to service the UI otherwise. Your application is busy with -performTaskforTime: and thus can't launch in time.
You can't block the main application thread for more than a few seconds. See this SO post.
Link
Related
I want to call a post method every 60 seconds later while application is in background mode.My objective is to store user location and send it to my server.So far, for test purpose I have used beginBackgroundTaskWithExpirationHandler and NSTime for every 3 seconds call of a temporary post call and it's working fine. But problem is, this background task is being stopped after 10 times call. but I want it to call it indefinite times in certain interval(suppose every 60s or once in a day).
Application iOS Deployment target : 9.0 and its Objective C project with swift compatibility. So far I have done this:
- (void)applicationWillResignActive:(UIApplication *)application {
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self endBackgroundUpdateTask];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[NSTimer scheduledTimerWithTimeInterval:3.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self targetMethod];
}];
}
can anyone help me out how can I call this targetMethod in regular interval?
Thanks
I'm trying to understand how the NSThread is working when the app is going to the background. I have the following code in appdeligate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self backgroundHandler];
}
- (void)backgroundHandler {
NSInteger counter=0;int scontinue=true;
while(scontinue){
NSLog(#"counter:%d",counter++);
sleep(1)
}
}
When I go to the background then it prints out every 1second a value. I have kept it open for about 5min and it gave me:
counter:1
counter:2
...
counter:300
And this keeps going. However if try to get into the foreground the backgroundHandler doesn't exit the while loop and my app doesn't respond to anything.
Now I change the applicationDideEnterBackground and instead I'm using a thread, i.e.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[NSThread detachNewThreadSelector:#selector(backgroundHandler) toTarget:self withObject:nil];
}
- (void)backgroundHandler {
NSInteger counter=0;int scontinue=true;
while(scontinue){
NSLog(#"counter:%d",counter++);
//sleep(1) : I remove the sleep for the shake of the example
}
}
Although I was expecting to have the same behaviour as in the previous case the thread seems to be hold after some ms. So what I had as a result is:
counter:1
counter:2
...
counter:30
And now the thread stucks at that point without executing anything. When I go to the foreground then the thread starts running again, i.e. counter increases and it is being printed out. The application runs again normally.
The above example is a rather simplistic version of what I'm tyring to do. What I actually want is when by app goes to the background to communicate with a server as long as the user doesn't go to the foreground. When it goes any communication should be terminated. So what I actually want is a combination of the simple examples above, i.e. when go to background in while loop keep asking the server, and when I enter to the foreground my app start responding normally and terminate the the backgroundHandler for loop.
Any help?
The main purpose of - (void)applicationDidEnterBackground:(UIApplication *)application is to save the state of the application when the application goes into background. In this if the application starts using a lot of the CPU or RAM, OS will terminate the app based on the state of the phone.
When you want to perform background operations or services to the server enable the background fetch by calling [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions of the AppDelegate.
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
//Add your code here
[self setBackgroundCheckInterval:UIBackgroundFetchResultNewData];
completionHandler(UIBackgroundFetchResultNewData);
}
- (void)setBackgroundCheckInterval:(NSInteger)resultCode
{
UIBackgroundRefreshStatus status = [UIApplication sharedApplication].backgroundRefreshStatus;
if (status == UIBackgroundRefreshStatusAvailable)
{
switch (resultCode)
{
case UIBackgroundFetchResultFailed :
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:BACKGROUND_CHECK_INTERVAL_NO_NEW_DATA];
break;
case UIBackgroundFetchResultNewData :
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:BACKGROUND_CHECK_INTERVAL_NEW_DATA];
break;
case UIBackgroundFetchResultNoData :
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:BACKGROUND_CHECK_INTERVAL_NO_NEW_DATA];
break;
}
}
}
My app syncs RSS feeds which takes about 15-30 seconds and for each sync I request beginBackgroundTaskWithExpirationHandler:. In iOS 7 and iOS 8 everything worked perfectly.
Starting with iOS 9 calling [[UIApplication sharedApplication] endBackgroundTask: backgroundTask]; causes the app to crash with Message from debugger:
Terminated due to signal 9.
From my research, signal 9 means the app using too much memory. When I use instruments, the app never goes over 30mb or 40% cpu.
I know that it's from calling endBackgroundTask: because if I don't call it, the app doesn't crash. Yet, once I call endBackgroundTask: the app will crash every time.
I'm not sure what's going wrong here. I've tried everything. Re-writting code, moving code around, commenting out everything except endBackgroundTask:. Any help or insight would be appreciated.
Here's the code:
#interface SyncClass ()
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
#end
-(void)startSync
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self beginBackgroundUpdateTask];
// I then call my syncing code [syncClass sync];
});
//When sync is done call endBackgroundTask
[self endBackgroundUpdateTask];
}
- (void) beginBackgroundUpdateTask
{
NSLog(#"Background Time:%f",[[UIApplication sharedApplication] backgroundTimeRemaining]);
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
NSLog(#"Ending background task");
}
Answering my own question here. AFNetworking and FMDB happened to be out of date. Updating them through cocoa pods seems to have fixed the problem.
I am using a serial dispatch queue to serialize some network requests when the user moves the app to the background.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
dispatch_queue_t opQ = dispatch_queue_create("com.myapp.network", NULL);
dispatch_async(opQ, ^{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
});
}
The problem is that when they run on this queue I have created, the app doesn't stay active even for the 5 seconds it is supposed to.
On the contrary, when I send the same requests outside of a queue, they are being sent for approximately 8 sec. but the app crashes afterwards.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
}
I would also like to write the remaining ones on the disk, so that they can be sent the next time the user opens the app.
What's the best way to implement this?
When the application enters the background if it requires additional time to complete some task you will want to notify the OS of that. The detailed documentation is here: http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html. Here's a quick and dirty patch.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier backgroundTask; //Create a task object
backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask:background_task];
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
}];
dispatch_queue_t opQ = dispatch_queue_create("com.myapp.network", NULL);
dispatch_async(opQ, ^{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
[application endBackgroundTask:background_task];
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
});
}
The bottom line is that you notify that the application needs to run in the background with beginBackgroundTaskWithExpirationHandler: THen when your done you call endBackgroundTask: to notify the OS that you are finished processing in the background. And finally make sure that you reset the backgroundTask variable to UIBackgroundTaskInvalid.
I am trying to do two things for a simple test app.
I am stuck at trying to learn how to use beginBackgroundTaskWithExpirationHandler
I want to execute a backgroundTask when the user presses the home button (nothing fancy). In 9 minutes, I'd like to alert the user that the time is about to expire (if possible) and allow the user to switch back into the app to renew the 10 minutes.
I don't need backward compatibility with iOS 3, or 4.
If you want your code to continue in the background, then you'll need to wrap it in a background task. It's also very important that you call endBackgroundTask when you're finished - otherwise the app will be killed after it's allotted time has expired
- (IBAction) buttonPressed: (id) sender
[self beingBackgroundUpdateTask];
// Do your long running background thing here
[self endBackgroundUpdateTask];
});
}
- (void) beingBackgroundUpdateTask
{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
Put the code in the applicationDidEnterBackground function in your UIApplicationDelegate. You will need to set up a UILocalNotification and schedule it. You should also probably disable it in applicationWillEnterForeground so it doesn't fire off the user goes back to the app before it expires.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UILocalNotification *timerNotification = [[UILocalNotification alloc] init];
//set up notification with proper time and attributes
[[UIApplication sharedApplication] scheduleLocalNotification:timerNotification];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
The cancelling code I gave there will actually cancel all notifications. If you have multiple notifications and only want to cancel a specific one, you should give the userInfo property of your notification a key/value when you set it up. Then, when the application enters the foreground, get the list of all active notifications by doing
NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
and loop through them, checking userInfo until you get to the one you want and then just cancelling that one with
[[UIApplication sharedApplication] cancelLocalNotification:whateverNotification];