app getting killed if using beginBackgroundTaskWithExpirationHandler IOS - ios

I am using beginBackgroundTaskWithExpirationHandler method in the applicationDidEnterBackground delegate method for keeping the NSTimer to keep running. But then the application gets killed after a long time if left in background for a long time (in my case 7-10 mins). I don't want my app to be getting killed and also I want the timer to run if in background. How do I get rid of this issue. Following is the code that I have written in the applicationDidEnterBackground method
- (void)applicationDidEnterBackground:(UIApplication *)application {
if ([application respondsToSelector:#selector(setKeepAliveTimeout:handler:)]) {
[application setKeepAliveTimeout:600 handler:^{
DDLogVerbose(#"KeepAliveHandler");
// Do other keep alive stuff here.
}];
}
/*
* The following code is used to make the app running in background state as certain features
* (eg: NSTimer) doesn not run if its in background or if the phone is locked
*/
UIBackgroundTaskIdentifier locationUpdater =[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:locationUpdater];
} ];
}

The beginBackgroundTaskWithExpirationHandler is only intended to let you complete a finite length task of a few minutes after the app leaves foreground. iOS, quite deliberately, doesn't let your app run in background perpetually except for very narrow situations (e.g. VOIP, audio app, navigation app) or for narrow functional needs (significant change location services, background fetch, etc.). But you cannot just run arbitrary code perpetually in the background.
For a discussion of the options, see the Background Execution section of the App Programming Guide for iOS.

Related

Understanding app background launch behavior

In apple Doc Understanding When Your App Gets Launched into the Background
Apps that support background execution may be relaunched by the system
to handle incoming events...
I am doing region monitoring and when I get that I am popping a UILocalNotification, but when I tap on UILocalNotification my app didReceiveLocalNotification is called. May be because my app is launched in background.
Second thing I did is I did not tap UILocalNotification and left for few minutes, means my app will terminate by iOS. I drag the notification center and then tap the UILocalNotification still my app enter in didReceiveLocalNotification.
The behavior I am expecting that app now launch in action of delayed UILocalNotification tap must enter in this method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification)
{
NSLog(#"notification caused app to launch, alert body = %#", notification.alertBody);
// do what ever action you want to do
// you could just copy the code from "didReceiveLocalNotification" and paste it here
}
return YES;
}
What is actually happening can please any one explain in detail?
Even after a delay when I tap UILocalNotification app do not enter in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Your app isn't terminated after a few minutes of inactivity.It becomes idle, and only terminated if memory is needed.Remember that it is launched to process the region change in the background, and it sends the notification in the first place, so it's resident in the device memory in this stage.Try to explicitly quiting it using the multitasking interface or launch several memory consuming apps (games usually do the trick) to force it to quit.A few minutes of waiting in the background won't make it terminate.

Transferring Data from Central to peripheral in Background mode in iOS

I am developing an application for a custom wearable communicating through BLE.
I have subscribed to the UI background modes in the info.plist file for Bluetooth-central.
I am transferring a firmware file of around 600 kb by dividing into chunk sizes of 200 bytes each. The process is going fine but as I am pressing the Home button the app is entering into background state and thus terminating the process after 1-2 minutes.
If my screens dims after certain amount of time then the firmware transfer continues but as soon as the home button is pressed the app stops transferring the data after few minutes.
Please help me out of this scenario.
Thanks.
To run task in background mode, you need to follow the belowed steps.
Step 1: Declare __block UIBackgroundTaskIdentifier bgTask as global variable.
Step 2: To add following code in applicationDidEnterBackground.
- (void)applicationDidEnterBackground:(UIApplication *)application {
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
}];
}
Step 3: Stop background task handler once apps come in foreground mode.
- (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] endBackgroundTask:bgTask];
}

iBeacon ranging in the background?

I have started to test out iBeacons using estimotes as beacons.
It's all running pretty good, but i'm struggling with getting the app to run properly in the background.
self.region = [[CLBeaconRegion alloc] initWithProximityUUID:self.uuid identifier: self.deviceID];
self.region.notifyEntryStateOnDisplay = YES;
[self.locationManager startMonitoringForRegion:self.region];
So this is the basic setup and for my test app i want to show a local notification when my phone is in immediate proximity of the beacon. My problem is that it won't work unless i include the line below.
[self.locationManager startUpdatingLocation];
Can anyone explain why that is or if i'm missing something about iBeacons?
You are mistaken. You don't need to call startUpdatingLocation in order to be called in the background.
When you're in the background it takes longer to get notified when you enter a region. If you want ranging calls, you have to issue the startRangingBeaconsInRegion call as well. As the other poster pointed out, you will only get a few seconds of ranging calls from the background when a new beacon is detected. (You get a didEnterRegion, followed by a few ranging calls, and then your app goes back to sleep.)
No, you are not missing anything. In background you app gets a very small amount of time to do ranging. From my personal experience, you get about 3 to 5 ranging callbacks. Thats it.
You do not need to call startUpdatingLocation method.
startMonitoringForRegion method starts monitoring the region only and call the didStartMonitoringForRegion delegate method to let you know when beacon enter/exit the region.
You need to call startRangingBeaconsInRegion method, which calls didRangeBeacons delegate method which gives you array of detected beacons with UUID, major, minor, rssi info of beacons.
For background execution, just use UIBackgroundTaskIdentifier and your code will work in background as well.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"=== DID ENTER BACKGROUND ===");
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
}];
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");
}
}

Know if GCD thread is running

In my iOS app I am download a fairly large amount of data in a background thread. What I need to do is somehow check when the user reopens the app if this task is still running or if it has been canceled.
If the user hits the home button for example, and then comes back to the app, the thread will continue which is good. But what if somehow the thread gets torn down by the OS. How can I on app resume know if a thread is running.
Thanks!
Some Code:
// Starts a background thread and also allows it to run in the background if the user exits app
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask:bgTask];
}];
// Code here to run in background
// Tell the main thread its done, and also remove itself from background execution
dispatch_async( dispatch_get_main_queue(), ^{
NSLog(#"Done with background thread");
[self endBackgroundUpdateTask:bgTask];
});
First off any thread, foreground or background will be suspended when your App goes into the background. There are some things you can do to get a small amount of time as you transition into the background to finish up and cleanup.... but your definitely getting shut down, "background" thread or not, when your app leaves the screen.
That means your download will have been suspended and you will have to gracefully recover and resume where you left off. For that you should investigate the techniques iOS offers for being notified your going into the background and being allowed some extra time to clean up and save some state that can be used to resume.
Investigate this:
beginBackgroundTaskWithExpirationHandler
Then use backgroundTimeRemaining to check how much time is left as you try to finish up in the extra time granted. When it's clear you can't finish in the allotted time save state and set a flag that indicates you need to continue or start over when the you re-launch. There is really no other way as iOS doesn't have any way of knowing if you finished what you were trying to do and it is up to you to

Preventing Screenshot/clearing cached data when iOS app gets backgrounded and resumed after a long time

I am building a cinema listing app, where the user can drill down thru the dataset to finally end up with a listing for a specific movie/theater/etc.
Now assume the user pauses using the app for 7 days. When reopening the app what he should not see are the listings from 7 days ago. But if the user just puts the app in background for a few minutes, the user should continue just where he left. I thought I could solve this issue by killing the app after a certain amount of time in background. This is the code:
static BOOL goingToQuit = NO;
#define KILL_IN_BACKGROUND_AFTER_SECS 300
- (void)applicationDidEnterBackground:(UIApplication *)application
{
goingToQuit = YES;
UIApplication* app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier __block bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
if(UIBackgroundTaskInvalid != bgTask) {
// Start the long-running task to kill app after some secs and return immediately.
dispatch_after( dispatch_time(DISPATCH_TIME_NOW, KILL_IN_BACKGROUND_AFTER_SECS * 1e09),
dispatch_get_main_queue(), ^{
if(goingToQuit) exit(0);
[app endBackgroundTask: bgTask];
});
}
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// cancel ongoing background suicide.
goingToQuit = NO;
}
What I see on my device is this: after KILL_IN_BACKGROUND_AFTER_SECS the app gets killed. After restarting the device logs show that the app got a new PID, entries showing the restart etc. Yet the device does not show the default.png startup image, but the screenshot of where the user has been before.
On the other hand if the user kills the application explicitely (double click on home button, tap & hold, click - on app) before he is restarting it the application starts with its default.png start up screen. This is the behaviour I want when killing the app programmatically.
Does anyone have an idea how to accomplish this? Any idea is highly appreciated.
BTW: As a workaround I tried to hide the main window during applicationDidEnterBackground and show it again on applicationWillEnterForeground. This, however, is highly confusing to the user when he is switching between apps.
In your app delegate's applicationDidEnterBackground you can display a view in front of the rest of your views, which the OS will grab as the last visible thing and which will be displayed when the app becomes active again (if it's still alive).
In your app delegate's applicationDidBecomeActive you can check to see if the data needs updating; if not then simply dismiss the view (animation is nice), and if so then first update (or just clear out) your data and then dismiss the view.
This is fairly common in apps. Many just use the default.png startup image, since users are accustomed to seeing it when the app launches normally.

Resources