Really confused about multitasking API in iOS, such as beginBackgroundTaskWithExpirationHandler etc.
For instance of recording video, when press button to start recording, we put
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
[self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}]];
}
when recording ends
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
[[UIApplication sharedApplication] endBackgroundTask:[self backgroundRecordingID]];
}
so who can explain what is meaning of these.
As you can see, when start recording, the block beginBackgroundTaskWithExpirationHandler is blank( just nothing to do ? ), and what is usage of those codes ?
Pretty much thanks for explaining.
The purpose of these calls is to let your app go into the background without being suspended as it normally would be. They request that the system grant your app some extra time to live, even though it has been backgrounded.
The thing you want to do in the background is whatever lies between beginBackgroundTask... and endBackgroundTask:. The important thing is that having announced the start of your lengthy task with beginBackgroundTask... you must subsequently call endBackgroundTask: to let the system know you're done and it is now okay to suspend you. You must always balance these calls, no matter what route your code takes. Be sure to cover all situations!
The expiration handler is not the thing you want to do in the background; it is, as FX rightly says, an error handler. It will be called in a dire emergency where you've exceeded your background time limit (nominally ten minutes). If it is called, this means that the system is going to suspend you right now and you can't stop it.
You should never have an empty expiration handler! The reason is that if you hit your time limit and your expiration handler is actually called, if it does not call endBackgroundTask:, the system will not only forcibly suspend your app, it will kill your app! So, your expiration handler must always execute very quickly and must minimally include a call to endBackgroundTask:.
Think of the expiration handler as any error handler. The system only allows the background application to use a limited amount of CPU time, so if you reach this limit, the background operation will be terminated, and your handler is called. Use it to clean up the internal state of your application.
Related
I'm making an app that receives constant updates (potentially hundreds of times a day) and, to make for a better user experience, it would be nice to have these downloaded in the background.
Looking at Apple's[1] documentation I need to set the background mode to "Background fetch". Exploring deeper you can read about the application:performFetchWithCompletionHandler[2] function which states that:
When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block... If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future.
The problem is our downloads will take longer than 30 seconds to download, and as such would rather not face the wrath of Apple sending updates fewer and farther between, thus exacerbating the issue!
So, am I able to do this somehow?
Also, I have created a crude experiment whereby I create a NSTimer:scheduledTimerWithTimeInterval to run every minute which logs to the console. This successfully works both on the iPhone in simulation (has been running for 30 mins plus) and also when I place it on a phone (a week plus)... why would this be!?
It may be hard to do because of the Apple 30s obligation. They decided so to eventually prevent big download to happen not to drain battery and data plan.
You must be sure you really need to download that much data (as it takes this long) in background, plus hundred times a day!
I mean, when your app goes foreground after a (long) background period, it may not be updated and it's normal case. So you need to do the update when the app goes foreground; only one update is needed. Otherwise, you should step back and rethink the update process.
Found a solution:
You can bypass Apple's application:performFetchWithCompletionHandler function and set your own timer. To do this make sure you do the following:
Have selected Background fetch under Your app settings > "Capabilities" > "Background Modes".
In AppDelegate.m, within application:didFinishLaunchingWithOptions, add the following code:
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
// Nothing
}];
You can now add a NSTimer in your AppDelegate and it will continue to run whilst in the background. E.g.,
_timerBg = [NSTimer scheduledTimerWithTimeInterval:1800
target:self
selector:#selector(bgFunction)
userInfo:nil
repeats:YES];
So, i want my app to do background execution for only a fixed amount of time, this is in case the user does not manually stop the app, and the app therefore in theory could run in background forever(is that even possible?).
I'm using the code below (just a test app) to test how long exactly a background task can run before ending. I read somewhere that 10 minutes is the longest we can do background execution, and there is no way to get beyond that(?). However, my code will only execute in the background for 3 minutes.
So to sum up my questions:
Is it possible to tell the app to execute in the background for x > 10 minutes?
2.Do i have any other options for something similar? (the actual app i need this implemented in, receives location updates in the background, the user could have the phone in the background for as long as 30 minutes, and suddenly not receiving updates would be bad)
- (void)viewDidLoad {
[super viewDidLoad];
counterTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
// do something }];
count=0;
theTimer=[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(countUp)
userInfo:nil
repeats:YES];
}
- (void)countUp {
if (count==10000) {
[theTimer invalidate];
[[UIApplication sharedApplication] endBackgroundTask:counterTask];
} else {
NSLog(#"asd");
count++;
NSString *currentCount;
currentCount=[[NSString alloc] initWithFormat:#"%d",count];
_theCount.text=currentCount;
long seconds = lroundf([[UIApplication sharedApplication] backgroundTimeRemaining]);
NSLog([NSString stringWithFormat:#"%ld",seconds]);
}
}
I read somewhere that 10 minutes is the longest we can do background execution, and there is no way to get beyond that(?). However, my code will only execute in the background for 3 minutes.
yes you are right before iOS 7 iOS allowed 10 minutes max for apps to execute in background , however since iOS 7 they have reduced this time to 180 seconds.
But if you want to get Location Updates in background than you can add Required Background modes property in your info.Plist file. Using this you will be able to run your app in background for getting location updates Apple will review your request while reviewing your app for app store submission so be sure to use this mode only if you using it for its actual purpose.
Following are various modes for which apple allows background execution you can take a look at it at Apples Doc on background execution
Edit
If you wish to stop getting location Updates after specific time once user goes to backGround you can do this
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self performSelector:#selector(stopGettingLocationUPdates) withObject:nil afterDelay:1800];
}
-(void)stopGettingLocationUPdates{
[self.locationManager stopUpdatingLocation]
}
This will stop updates after 30 mins.
Your code is not running in the background. It is not testing what you want to test.
Apple's docs say:
Executing a Finite-Length Task in the Background Apps that are
transitioning to the background can request an extra amount of time to
finish any important last-minute tasks. To request background
execution time, call the
beginBackgroundTaskWithName:expirationHandler: method of the
UIApplication class.
The actual time you get is not specified and is probably decided ad hoc based on power consumption, memory needs and so on. They may be a maximum.
They go on to say:
Implementing Long-Running Background Tasks For tasks that require more
execution time to implement, you must request specific permissions to
run them in the background without their being suspended. In iOS, only
specific app types are allowed to run in the background:
Apps that play audible content to the user while in the background, such as a music player app
Apps that record audio content while in the background.
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
Apps that implement these services must declare the services they support and use system frameworks to implement the relevant aspects of
those services. Declaring the services lets the system know which
services you use, but in some cases it is the system frameworks that
actually prevent your application from being suspended.
For the bounty, I am not interested in GPS or audio background modes as the former uses too much of the battery and the latter prevents any other audio from being used, otherwise facing audio interruption, thus ending background processes. I will need a way to be continuously processing in the background, so background modes that trigger occasionally are also out of the question.
If there is some way to run the application in the background, even by ignoring Apple's rules, I am interested in trying it. If the answer is VOIP, I am unsure where to begin the implementation, as all of my research has come up too high level or as a failure. How will my application be able to run in the background using the VOIP background mode. Without any added code, the application refuses to run in the background.
I know that with iOS 7, background modes have changed again. I would like to be able to run my application (that will never need to be approved on the iOS App Store) in the background. I would also like to be able to stop execution in the background until a specific time in the future.
For example, I would like it to run a process for 15 minutes, schedule the next task and then sleep until that time. For now, I've had to run a silent track in the background for background processing, but I would like to be able to have the application truly sleep during that time - also, playing real music or making a phone call are "handy features" of the iPhone and I don't like losing them.
I know there is also GPS, but that consumes an enormous amount of battery. The other background modes don't seem to give full control of background processing and timing to the application and leave a large portion of the timing and execution duration to the OS.
What I need is to be able to have my application process in the background for minutes at a time and then sleep until a fairly specific interval and continue processing. Is this possible with a better approach than I am currently using?
I've seen that VOIP used to be a possibility, but I'm not sure that it will work, as I don't need the application to run one simple task in the background, but rather to continue whatever was running in the foreground before the application was pushed to the background. Also, individual tasks could take upwards of 1 hour to complete, so they won't be able to transfer when the background task expires. All of my assumptions are based off this thread.
Edit: There seems to be a terrible drop off rate with this method. At random, the recursion will seemingly fail for seemingly no reason (maybe a system timeout on execution?). If I place the recursion before ending the background task, the OS kills my application, but if I put it after, it occasionally seems to stop the background tasks at some point. I have seen it stop in the middle of my "allotted background time", as well.
In short, the below method does seem to run indefinitely, but not infinitely. Is there either a way to make the runtime guaranteed to be infinite or another solution?
It seems that using VOIP was leagues easier than I had first thought.
All that is required to run my application indefinitely (unfortunately sleeping is not an option) is to add voip to the selected Background Modes, either in the plist or in Target's Capabilities. After that, adding and running this code once, in an object that is never deallocated (your AppDelegate works nicely here), will allow for infinite background processing time:
- (void)infiniteBackgroundLoop
{
__block UIApplication *applicationBlockReference = [UIApplication sharedApplication];
__block AppDelegate *appDelegateBlockReference = self;
__block UIBackgroundTaskIdentifier backgroundTask = [applicationBlockReference beginBackgroundTaskWithExpirationHandler:^
{
[applicationBlockReference endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
[appDelegateBlockReference infiniteBackgroundLoop];
}];
}
In order to allow sleeping indefinitely, add a break to the recursion.
I used background fetch to achieve something similar.
You can use this to keep your app active in the background.
I have the a demo, see if it helps you:
Add these properties to your .h file -
#property (nonatomic, strong) NSTimer *updateTimer;
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
Now suppose you have a action on button --> btnStartClicked then your method would be like :
-(IBAction)btnStartClicked:(UIButton *)sender {
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(calculateNextNumber)
userInfo:nil
repeats:YES];
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"Background handler called. Not running background tasks anymore.");
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
}
-(void)calculateNextNumber{
#autoreleasepool {
// this will be executed no matter app is in foreground or background
}
}
and if you need to stop it use this method,
- (IBAction)btnStopClicked:(UIButton *)sender {
[self.updateTimer invalidate];
self.updateTimer = nil;
if (self.backgroundTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
i = 0;
}
We also played with background modes in our app, and I check all solution that found, and can say that there is only one way to stay active in background and is not "VOIP", because "VOIP" gives your app wake-up every 5-6 minutes not infinity run.
In documentation about setKeepAliveTimeout:handler: you can see that this method will call handler block at minimum every 600 second, and block has a maximum of 10 seconds to perform any needed tasks and exit.
To clean this you can add NSLog(#"time remain: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]); to your infiniteBackgroundLoop implementation. Because second and next call beginBackgroundTaskWithExpirationHandler not get same time of background run as first call.
Another working way is Location Manager, yes is bad because use lot of battery but is get you that you want - app newer stop.
The implementation can be easily found there
You could use background fetch and set the regresh rate to a short NSTimeInterval.
In your didFinishLaunchingWithOptions: try to add:
[[UIApplication sharedApplication] setMinimumBackgroundFetch:1];
I haven't tested this, let me know if this could be a starting point.
I have an iOS application that inserts records to an Azure Mobile Service database periodically. When the application becomes inactive (i.e. when my app delegate's applicationWillResignActive method is called) I would like to insert a record to tell my AMS application that the client session has ended.
When I try to insert a record from either applicationWillResignActive or applicationDidEnterBackground the completion block is never reached. To keep the app delegate method from returning before the AMS insert completes, I've tried calling usleep to delay 100ms in a loop while checking for a flag that I set in the insert completion block.
I suspect that a run loop or operation queue needed by the AMS client library isn't being serviced when I'm in this state, but the AMS client library docs don't have enough detail to tell if that's the case.
This seems like a pretty common scenario - has anyone else encountered this?
I'm pretty certain that making a network request from applicationWillResignActive is frowned upon by iOS. This method is used to clean things up, not make use of additional resources. You can however request that iOS keeps your app running in the background for some period of time. You should be able to do something like this:
UIBackgroundTaskIdentifier taskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
//Perform call to mobile service
myservice.dosomething(Completion:^{
//Call to mobile service is done
[[UIApplication sharedApplication] endBackgroundTask:taskID];
});
}];
That should enable you to make the call and then get the response back. Remember though that iOS can still kill your app before this completes if it chooses to.
I am trying to keep the iOS app in active state for more than 10 mins when it enters in background state.
How can I implement this.
See "Background Execution" section of the iPhoneAppProgrammingGuide. In short, your app must be one of these types:
Apps that play audible content to the user while in the background, such as a music player app
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Newsstand apps that need to download and process new content
Apps that receive regular updates from external accessories
And you must add to the Info.plist as follows:
Add the UIBackgroundModes key to your
Info.plist file and set its value to an array containing one or more of the following strings:
audio—The app plays audible content to the user while in the background. (This content includes streaming audio or video content using AirPlay.)
location—The app keeps users informed of their location, even while it is running in the background.
voip—The app provides the ability for the user to make phone calls using an Internet connection.
newsstand-content—The app is aNewsstand app that downloads and processesmagazine or newspaper
content in the background.
external-accessory—The app works with a hardware accessory that needs to deliver updates on a
regular schedule through the External Accessory framework.
bluetooth-central—The app works with a Bluetooth accessory that needs to deliver updates on a
regular schedule through the CoreBluetooth framework
Note that part of the review process will be checking to make sure that your app does what it says it's doing with regard to background processing.
Here's what I've done using beginBackgroundTaskWithExpirationHandler.
Write a method that starts a background task.
Inside that background task, run a NSTimer with a scheduled (non repeating) time that is under 10 minutes. For the purposes of my situation I was using 5 minutes.
Once the NStimer's selector fires, end the background task and then instantly call the method that you wrote earlier to start off another background task.
If you want to schedule methods to run at specific times, you will have to check for them in the background task.
This solution isn't really ideal and is still power hungry but will do what you want.
Edit: Since iOS7, I suggest you read this excellent post. Note that this article was last updated in 2013 and is probably irrelevant now.
Only certain types of apps are allowed to run in the background. See the "Implementing Long-Running Background Tasks" section of this guide.
If you aren't requesting permissions to do background processing you can use UIApplication's beginBackgroundTaskWithExpirationHandler but you cannot get extra time.
This code makes your iOS app run indefinitely in the background. Copy and paste the below methods into a singleton / manager which handles the tasks you need to perform in the background.
// #interface
// Declare Private property
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
//#end
// ...
// Copy into
//#implementation
- (void)setupBackgrounding {
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(appBackgrounding:)
name: UIApplicationDidEnterBackgroundNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(appForegrounding:)
name: UIApplicationWillEnterForegroundNotification
object: nil];
}
- (void)appBackgrounding: (NSNotification *)notification {
[self keepAlive];
}
- (void) keepAlive {
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
[self keepAlive];
}];
}
- (void)appForegrounding: (NSNotification *)notification {
if (self.backgroundTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
}
You can't. Unless your app uses audio, voip or gps. What you can do is notify the user (via local notifications) that the time is almost up and ask him to open/close the app.
Also if you just need to notify the user, you can use push notifications.
https://github.com/yarodevuci/backgroundTask Check my code here I am using audio player that plays blank wav file Works perfectly on IOS 8 Battery usage around 10% in 24 hour period
How to use:
var backgroundTask = BackgroundTask()
backgroundTask.startBackgroundTask() //Starts playing blank audio file. You can run NSTimer() or whatever you need and it will continue executing in the background.
backgroundTask.stopBackgroundTask() //Stops the task
Warning: Apple will reject this if you try to submit it!
If your App type is not one of VOIP/Audio/Location....(check Background Modes),
or you don't want to specify your App as a background App, you can implement beginBackgroundTaskWithName:expirationHandler or beginBackgroundTaskWithExpirationHandler to ask for more time to run your process in background. You can find the detailed description here
Apps moving to the background are expected to put themselves into a quiescent state as quickly as possible so that they can be suspended by the system. If your app is in the middle of a task and needs a little extra time to complete that task, it can call the beginBackgroundTaskWithName:expirationHandler: or beginBackgroundTaskWithExpirationHandler: method of the UIApplication object to request some additional execution time. Calling either of these methods delays the suspension of your app temporarily, giving it a little extra time to finish its work. Upon completion of that work, your app must call the endBackgroundTask: method to let the system know that it is finished and can be suspended.
Each call to the beginBackgroundTaskWithName:expirationHandler: or beginBackgroundTaskWithExpirationHandler: method generates a unique token to associate with the corresponding task. When your app completes a task, it must call the endBackgroundTask: method with the corresponding token to let the system know that the task is complete. Failure to call the endBackgroundTask: method for a background task will result in the termination of your app. If you provided an expiration handler when starting the task, the system calls that handler and gives you one last chance to end the task and avoid termination.