Please take a look at this very simple piece of code:
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 100; i++)
{
NSLog(#"LOOP %d", i);
sleep(1);
}
});
If I send my app to the background state it is still running. Yet if I put the execution onto a non main queue like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (int i = 0; i < 100; i++)
{
NSLog(#"LOOP %d", i);
sleep(1);
}
});
then the execution is suspended when my app is going to the background. Is it possible to make it run in a background state when dispatched on a non main queue?
I need to mention that I'm running my app with these background modes enabled:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>voip</string>
</array>
In addition to using performExpiringActivityWithReason, you can use the UIBackgroundTaskIdentifier API:
// Perform the task on a background queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Request the task assertion and save the ID.
self.backgroundTaskID = [[UIApplication sharedApplication]
beginBackgroundTaskWithName: #"Finish Pending Tasks" expirationHandler:^{
// End the task if time expires.
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
self.backgroundTaskID = UIBackgroundTaskInvalid;
}];
// Add your code here.
// End the task assertion.
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
self.backgroundTaskID = UIBackgroundTaskInvalid;
};
The expiration handler is what gets called before the app is killed, but don’t plan on doing computationally expensive tasks using this method since the OS has system-wide time limits which are out of the developer’s control.
If you need to perform specific tasks in the background for certain system events like network fetches, then consider using a Background Mode.
The advantage of using the UIKit API, in this case, is you can query [[UIApplication sharedApplication] backgroundTimeRemaining] within your for loop to perform any last-minute cleanup steps.
Note that if you’re using App Extensions Apple recommend using the NSProcess API, so advice will vary depending on the use case.
Please try using NSProcessInfo performExpiringActivityWithReason API
[[NSProcessInfo processInfo] performExpiringActivityWithReason:#"myReason" usingBlock:^(BOOL expired)
{
// This block is run on a separate (background) thread
// Put your code here...
}
please note this is only a request for some additional CPU time on a process that is being backgrounded ... you cannot run background code indefinitely.
The block will be invoked a 2nd time with expired == YES when you're about to be killed.
Related
I am trying to collect coreMotion acceleration data in the background for longer than 10 minutes. This must be possible since apps like Sleep Cycle do this.
I just want to make sure this is allowed though, since it does not seem to be one of these:
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.
However, I have tried following these steps to get a background task, but I am thinking there is a better way for CoreMotion:
Header:
UIBackgroundTaskIdentifier bgTask;
Code:
// if the iOS device allows background execution,
// this Handler will be called
- (void)backgroundHandler {
NSLog(#"### -->VOIP backgrounding callback");
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(#"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
- (void)applicationDidEnterBackground:(UIApplication *)application {
BOOL backgroundAccepted = [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{ [self backgroundHandler]; }];
if (backgroundAccepted)
{
NSLog(#"VOIP backgrounding accepted");
}
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(#"BGTime left: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self doSomething];
sleep(1);
}
});
}
Use this one:
Apps that keep users informed of their location at all times, such as a navigation app
In other words, you make a location manager and tell it to start doing updates. You don't have to do anything with those updates! But as long as this is happening - that is, as long as your app is continuing to do location updates in the background - your app is also allowed to use Core Motion in the background. This is not just a trick; it is official Apple policy as explained in one of the WWDC videos from a couple of years ago.
I have this code who send data and receive even when the application is in background mode (minimized app):
MyViewController.m
-(void)viewDidAppear:(BOOL)animated{
[self doUpdateEvenAppMinimized];
}
- (void) doUpdate{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self beginBackgroundUpdateTask];
[self sendFilesToServer];//Inside this method have a sleep that call every 5 minutes
//The code used in sendFilesToServer is the same in this website https://dcraziee.wordpress.com/2013/05/29/how-to-upload-file-on-server-in-objective-c/
//[self endBackgroundUpdateTask];//This method is forever...so I not need to call this line
});
}
- (void)beginBackgroundUpdateTask{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
The documentation says that the maximum time is 10 minutes, and to remove it I use the concept of Implementing Long-Running Tasks, For that I select my project > capabilities > Background Modes (Turn On) > External accessory communication (Checked).
With these steps, my application will be exempt from the 10 minutes?
Trying to circumvent the rules to run in the background sounds like the wrong approach. Consider using NSURLSession for long running networking operations that are not tied to the lifetime of your app.
I am trying to run the process in background thread . I want that process to finish in 60 secs and start running again.No matter application is in foreground or background. I don't know how to implement and where to implement it.I'm using ios7.In that process I'm also taking location updates.
I read about the background tasks, but it wasn't giving proper idea of the process. Can someone provide me with good source or link?
There is no such api given by ios for background process unlike android which use service for that.You can use timer for continuos background process .Also there is dispatch_async ,selector in background for efficient background processing.
Hope this helps.
you can use something like this for background processing,but remember apple has put restriction of 10-15 min to complete the processing.
UIApplication* app = [UIApplication sharedApplication];
task = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:task];
task = 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.
NSLog(#"Started background task timeremaining = %f", [app backgroundTimeRemaining]);
if (connectedToNetwork) {
// do work son...
}
[app endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
});
Also you can check the following :
**BOOL backgroundSupported = NO;
if ([device respondsToSelector:#selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;**
As we know there are some limitations for applications which running in background mode.For example, the NSTimer doesn't work. I tried to write a "Timer" like this which can work in background mode.
-(UIBackgroundTaskIdentifier)startTimerWithInterval:(NSTimeInterval)interval run:(void (^)())runBlock complete:(void (^)())completeBlock
{
NSTimeInterval delay_in_seconds = interval;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, delay_in_seconds * NSEC_PER_SEC);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// ensure the app stays awake long enough to complete the task when switching apps
UIBackgroundTaskIdentifier taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
completeBlock();
}];
NSLog(#"remain task time = %f,taskId = %d",[UIApplication sharedApplication].backgroundTimeRemaining,taskIdentifier);
dispatch_after(delay, queue, ^{
// perform your background tasks here. It's a block, so variables available in the calling method can be referenced here.
runBlock();
// now dispatch a new block on the main thread, to update our UI
dispatch_async(dispatch_get_main_queue(), ^{
completeBlock();
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
});
});
return taskIdentifier;
}
I called this function like this:
-(void)fire
{
self.taskIdentifier = [self startTimerWithInterval:10
run:^{
NSLog(#"timer!");
[self fire];
}
complete:^{
NSLog(#"Finished");
}];
}
This timer works perfect except that there is one problem. The longest period for background task is 10 minute.(Please refer to the NSLog in startTimerWithInterval).
If there any way to make my timer work more than 10 minutes? By the way, my application is a BLE application, I set UIBackgroundModes to bluetooth-central already.
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.