So, my ideal scenario is that I want my MCNearbyServiceAdvertiser and MCNearbyServiceBrowser to work in the background. But I understand that these need to be killed when application resign active, and resume the work when i come back to foreground.
But what is bugging me is that, when i CONNECT two peers to a session, and start chatting between them, it suddenly disconnects. Meaning.. my MCSession object disconnects when it goes to background. I was also told of the Apple bug with certificateHandler(YES);, and i explicitly call it now.
I want to set this up exactly how FIRECHAT does it. Can anyone give me some pointers on why its keeps failing, or how you guys manage to keep this active?
Thanks,
In order to for your peers to stay connected you need to start a background task when the app enters the background, otherwise iOS will tear down the network connections and suspend the app.
- (void)applicationDidEnterBackground:(UIApplication *)application {
//Start a background task to keep the app running in the background
self.bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
//If your background task takes too long, this block of code will execute
[self cleanUp];
self.bgTask = UIBackgroundTaskInvalid;
}];
//Do the work you need to do
dispatch_async(dispatch_get_main_queue(), ^{
//Finish up the transfer of data between peers
[self cleanUp];
//End the background task so that iOS doesn't kill the app
[application endBackgroundTask:_bgTask];
});
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[application endBackgroundTask:_bgTask];
}
- (void)cleanUp {
//Clean up the Multipeer session
}
Note that this only applies to existing connections that have been made while the app was in the foreground. You still have to stop the browsing and advertising when entering background.
Related
I was wondering if I could send some webservice calls while my application is in the background. How does skype do it? Even if I press the home button my call stays connected.
Building on what rckoenes stated, applications are allowed to register background tasks to be completed after the user hits the home button. There is a time limit of 10 or 15 minutes for these tasks to complete. Again, you can register a task to complete immediately after the user hits home, this does NOT allow you to execute code say an hour after they exit the app.
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;
});
UPDATE: if your app supports versions of iOS previous to iOs 4, you should also check to ensure that multitasking is supported before registering a background task. Use something along the lines of:
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:#selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
Try This... Excellent code for running app in background with no time limit. (I tested it for downloading more than 600 mb data from web-service.)
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
Update ::
you can found more information regarding multitaksing in this apple doc Background Execution.
Please test on device.
It depends or what kind of application are you trying to code.
Skype is registered as a VoIP (Long-running app) app and this is why it can stay "running" although it is on the background.
Apple separates apps in three:
Executing Finite-Length Tasks (you can run tasks for a finite amount of time)
Downloading Content in the Background (you can download content to present it to the user when the app becomes active again)
Implementing Long-Running Tasks (This is the most interesting background apps category, with some subcategories that the developer should define for your app)
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) (SKYPE is here)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
So, you need to evaluate in which category your app is and what your service operation performs. Maybe if you're sending some small things to the service the best approach is only to request some extra time on the background for doing the job.
More info about all of this are on this link:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
I wrote an app the monitors a user's location. Location services are turned on when my view loads as such:
// Create the location manager if this object does not
// already have one.
if (nil == self.locationManager) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startMonitoringSignificantLocationChanges];
NSLog(#"Started monitoring significant location changes");
If I terminate the app while its active, the location services stop. Here is the code I wrote to stop the location services in the AppDelegate.m:
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
NSLog(#"entered terminate in delegate");
[myController.locationManager stopUpdatingLocation];
[myController.locationManager stopMonitoringSignificantLocationChanges];
myController.locationManager = nil;
[self saveContext];
}
I ran into a problem such that if my app is already in the background, the above method is not called and as such I could not turn off location services. To work around this, I found this solution which I tried:
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(beginBackgroundTaskWithExpirationHandler:)]) {
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
// Synchronize the cleanup call on the main thread in case
// the task actually finishes at around the same time.
dispatch_async(dispatch_get_main_queue(), ^{
if (self.bgTask != UIBackgroundTaskInvalid)
{
NSLog(#"Marking bgTask as Invalid when we entered background");
[app endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}
});
}];
}
}
So the above solution works if my app was in the background. However, I noticed that if leave my app running in the background for a long time, more than five minutes, the expiration handler kicks in. So then if I terminate the app without bringing it to the foreground. The location services icon still appears on the phone. I have to restart the app or bring it to the foreground first and then terminate it for the code that disables location services kicks in.
If I remove these two lines:
[app endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
Then stopping location services works find after five minutes while the debugger is attached. If I leave it running longer in the background then the terminate code never kicks in. Is it because I am not changing locations or does the app eventually die?
So my question is, is there another way to make sure that the app properly stops location service monitoring if its been in the background for a while?
Thank you...Amro
Edit, I did more experiments and here are my findings:
While attached to the debugger if I wait 11 minutes from time it enters background mode, the method willTerminate gets called:
2015-01-13 08:52:45.935 [441:37074] ###AMRO--->applicationWillResignActive entered
2015-01-13 08:52:46.642 [441:37074] ###AMRO--->Entered background mode
2015-01-13 08:55:42.697 [441:37074] ###AMRO--->beginBackgroundTaskWithExpirationHandler called
2015-01-13 09:03:26.265 [441:37074] entered terminate in delegate
If I try this without debugger, and only wait four minutes, the terminate function does not get called, I don't have to wait the whole 11 minutes.
From the Apple's documentation:
App Termination
Apps must be prepared for termination to happen at any time and should
not wait to save user data or perform other critical tasks.
System-initiated termination is a normal part of an app’s life cycle.
The system usually terminates apps so that it can reclaim memory and
make room for other apps being launched by the user, but the system
may also terminate apps that are misbehaving or not responding to
events in a timely manner.
Suspended apps receive no notification when they are terminated; the
system kills the process and reclaims the corresponding memory. If an
app is currently running in the background and not suspended, the
system calls the applicationWillTerminate: of its app delegate prior
to termination. The system does not call this method when the device
reboots.
In addition to the system terminating your app, the user can terminate
your app explicitly using the multitasking UI. User-initiated
termination has the same effect as terminating a suspended app. The
app’s process is killed and no notification is sent to the app.
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html#//apple_ref/doc/uid/TP40007072-CH2-SW1
About significantChangesLocation:
If you leave the significant-change location service running and your
iOS app is subsequently suspended or terminated, the service
automatically wakes up your app when new location data arrives.
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html
If you don't want your app waked up by significantChangeLocation, you could call stopMonitoringSignificantLocationChanges when backgroundTimeRemaining is about to expire.
Att.
I know this may seem a duplicate subject but its not (hopefully its not)
We have an application that uses GameCenter in the background for backend communication, up until ios8, GameCenter has ran in the background fine.
Now I need to find a fix around this, the key is that the app needs to constantly run in the background, it uses GameCenter chat server etc, so we have enabled Audio. I can't see us being able to use VOIP as an option (Apple would probably reject it)
I tried
[self performSelectorOnMainThread:#selector(keepAlive) withObject:nil waitUntilDone:YES];
[application setKeepAliveTimeout:600 handler: ^{
[self performSelectorOnMainThread:#selector(keepAlive) withObject:nil waitUntilDone:YES];
//[[GCHelper sharedInstance] applicationWentBackground];
}];
- (void)keepAlive {
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"Background handler called. Not running background tasks anymore.");
// [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
// self.bgTask = UIBackgroundTaskInvalid;
[self keepAlive];
}];
}
Which keeps it going for a bit, I guess the main question is, is it even possible now?
I have also followed this guide
http://blog.dkaminsky.info/2013/01/27/keep-your-ios-app-running-in-background-forever/
As long as the app itself is in the foreground, you can fire a notification every xx seconds which handles the background tasks.
When the full app goes into background, you only have a limited amount of time to close actions.
I know this is a very common question. I have read many answer but not found out the appropriate answer for me. That's why I post this question and hope someone will show me how to fix my code.
I have function startUpdate to update location using CLLocationManager. In applicationDidEnterBackground method, I write something like below:
[self startUpdate]; // position1
NSLog(#"applicationDidEnterBackground");
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[self startUpdate]; // position2
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self startUpdate]; // position3
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://google.com"]];
});
I tried to put [self startUpdate] at one of three positions (position1, 2, 3) and sometime it works, sometime not, and I do not know why.
In case it works, updating just run in 3 minutes event. If I call startUpdate when app is in foreground, then put app to background, updating will last 15 minutes with real device, and more than 1hour with simulator ( I don't know exactly, after 1 hour, i thought it would last forever then I stop testing). So what is different between: startupdate in foreground-> go to background vs startupdate in background; simulator vs real device?
right after position3, I called following line to open safari
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://google.com"]];
But it does not work. So what types of task can be executed here, in background?
I need to keep a service running forever. Some search results say that it's impossible. But some familiar apps did that (Facebook,... keeps service to receive notifications). How can they do that?
Thank you so much,
The exact behaviour of location services has been clarified in the latest update to the Core Location documentation. It now states -
The standard location service delivers events normally while an app is
running in the foreground. When your app is in the background, this
service delivers events only when the location-updates background mode
is enabled for the app. This service does not relaunch iOS apps that
have been terminated.
The significant location change service delivers events normally while
an app is running in the foreground or background. For a terminated
iOS app, this service relaunches the app to deliver events. Use of
this service requires “Always” authorization from the user.
So, it seems that for the best chance of continuing to receive location updates in the background you should switch to significant location change monitoring once you move to the background state and restore full location monitoring once you return to the foreground.
During a background task execution what will happen if the user kills the app (which already is in background mode)?
Picture this:
The app starts task X (with 10 min background support and a expiration handler that has to be called).
Then, the app goes to background and the user kills the app.
I'm confused on what will happen to task X after the app is killed. Does it still have that background time to execute? Does the expiration handler ever gets called?
If the app is "already in background", the user has already "closed the app"! So what can your question mean? You've already gone into the background, and if you called beginBackgroundTaskWithExpirationHandler: as you did so, things proceed normally.
Do you mean the user forcibly kills the app in the background, by summoning the "recent apps" interface and going into jiggy mode and deleting the app from the "recent apps" interface? Then the app is summarily killed; you get no notification and whatever you were doing is interrupted.
Moreover, the only thing the expiration handler block is supposed to do is call endBackgroundTask:. If you are summarily killed, the fact that you are unable to make this call is unimportant!
Ok so this is the result
In this case OS will send a SIGKILL signal to your app's process and applicationWillTerminate method is not called.
Below is just my interpretation from Apple docs, guess work and Google results.
In such case below method of your application delegate will get called
- (void)applicationWillTerminate:(UIApplication *)application
Quote from Apple Docs
For applications 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 application. 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.
So you have to UIApplicationExitsOnSuspend value to YES in your plist file otherwise there is no guarantee that applicationWillTerminate: will ever get called. That is why the doc has may used in.
I dont think expiration handler block will be called, though I am not sure.
This is pretty easy to test, so I just did (on an iPhone 4S running iOS 6.1.3) using code I'll paste at the end, which starts a background task in the app delegate's applicationDidEnterBackground method.
The result is surprising.
When the user exits the app by clicking the Home button, then manually kills the app (by double-clicking Home, putting things in jiggy mode and hitting the app's close icon) the following occurs:
applicationWillTerminate is called.
When applicationWillTerminate exits, background execution is terminated, regardless of how much execution time the background task had left. The app has been killed.
HOWEVER..
If you arrange things so that applicationWillTerminate doesn't exit after being called, as in my code below, the following happens -- at least it does on my test setup -- when the app is manually killed:
The app's background task continues to run in the background.
Even when the allocated background execution time has expired, the background task continues to run, as does the code in applicationWillTerminate, until that method exits.
This is clearly a bug -- you shouldn't be able to continue to run code forever -- and I wouldn't rely on it always working. But those who've been using various hacks around playing audio in the background to keep an app alive might want to investigate. I'd be interested if other people try the code on different iOS versions/devices and get the same results.
Code for AppDelegate.m in my test project:
//
// BTAppDelegate.m
// BackgroundTest
//
// Created by David Fearon on 07/05/2013.
// Copyright (c) 2013 David Fearon. All rights reserved.
//
#import "BTAppDelegate.h"
#implementation BTAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"application didFinishLaunchingWithOptions called");
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"applicationWillResignActive: called");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"applicationDidEnterBackground: called");
UIApplication* thisApp = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier __block task = [thisApp beginBackgroundTaskWithExpirationHandler:^{
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self printTimeRemaining];
while(YES) {
[NSThread sleepForTimeInterval:1.0];
[self printTimeRemaining];
}
//[thisApp endBackgroundTask:task];
});
}
-(void)printTimeRemaining{
NSLog(#"Background task time remaining: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"applicationWillEnterForeground: called");
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"applicationDidBecomeActive: called");
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#"applicationWillTerminate: called");
while(YES) {
[NSThread sleepForTimeInterval:1.0];
NSLog(#"Still executing code in applicationWillTerminate.");
}
}
#end