What I want to do is to tell the user to press the home button and reopen the app, which requires the app quitting upon entering the background - it's for a reset app settings thing. But I do want the app to background in all other times. Do I use the app delegate?
If there is an easier way, e.g. somehow restarting the main view controller then that would be helpful.
For your app to run in background always use this code.
Put this code under the method ApplicationDidEnterBackground in your app delegate class .m file
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
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'm developing an app which sends notifications when you are nearby of promoted places.
My problem is when I go to background and then I quit the app, I don't want the location services working when the app doesn't work (but I want them to work in background).
I saw only 3 apps which close the gps when the app is closed and I want to know how they did that, Facebook, Google Maps and Apple Maps, not Foursquare, not FieldTrips...
Thank you everybody.
you can add an observer for UIApplicationWillTerminateNotification where you start locationManager and than stop location updates
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:nil];
method to perform when you receive the notification
- (void)applicationWillTerminate:(NSNotification *)notification {
//stop location updates
}
I found the correct answer to my question becouse of #GuyS second post:
Adding that in your AppDelegate.m applicationDidEnterBackground
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(beginBackgroundTaskWithExpirationHandler:)]) {
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 (bgTask != UIBackgroundTaskInvalid)
{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
});
}];
}
}
And declaring that variable:
UIBackgroundTaskIdentifier bgTask;
After that you only have to stop your location services in applicationWillTerminate...
Thank you for your replies.
The solution provided by #GuyS in this topic should work. I'm getting the UIApplicationWillTerminateNotification in case the app is in background and then I close it by swiping up the snapshot. Please check whether you work correctly with NSNotificationCenter (especially adding and removing notification). Plus, please check the object you subscribed on the notification is alive when the app is in background.
Another similar solution is to place the code that disables GPS in appropriate UIApplicationDelegate callback in your AppDelegate method.
- (void)applicationWillTerminate:(UIApplication *)application {
//stop location updates
}
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 notice in my app that when application enter background while loading causes the error such as "timeout" or "host name not found" .
It is due to the process that does not allow connection to run in background for a long time.
But that kind of error message make it bad for user experience. So what should I do to cancel the transaction ? Should I just cancel all the connection ? I tried to search the Q&A in SO here but can't find an answer.
For more information, my app use NSURLConnectionDelegate Method. I have a store singleton that manage all connection to my server. NSURLConnection is called and managed in custom object also.
I tried to just [connection cancel] in - applicationDidEnterBackground: but that make the UI broken because I load data to put into UITableViewCell ,etc. Can anyone point to the example to solve this kind of problem?
Updated Code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier backgroundTask; backgroundTask =
[application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid; }]; }
}
Can I just put this code in the appDelegate ? What is the drawback for just doing this versus put the beginBackgroundTaskWithExpirationHandler before the task that I want to keep running in background and endBackgroundTask after that task finished ? My code has one object that deal directly to NSURLConnection.
You are allowed to keep running an NSURLConnection for some period of time after you go into the background. Apple doesn't publish the exact period of time, but it's 10 minutes. See Executing a Finite-Length Task in the Background for details on how to use beginBackgroundTaskWithExpirationHandler: to request more time to complete your download.
In most cases you shouldn't proactively cancel your download. You should wait until the system expires you and then deal with the error at that point. If your download is brief, there's no reason to cancel it (in most cases, the most expensive thing about a connection is setting it up in the first place). If it's a very long download, then the user is going to be annoyed if it doesn't proceed in the background. (This assumes that you're downloading things because the user requested it.)
First, it's better to have this call on the application delegate, so that the View in the NavigationController can be closed. Second, mark beginning of the background processing with beginBackgroundTaskWithExpirationHandler: and end it with endBackgroundTask: like this:
.h:
UIBackgroundTaskIdentifier bgTask;
.m:
- (void)sendPhoto:(UIImage *)image
{
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
NSLog(#"Sending picture...");
// Init async NSURLConnection
// ....
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Picture sent.");
UIApplication *app = [UIApplication sharedApplication];
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
Also remember one thing its important:
You have 10 minutes before iOS terminates your app. You can check this time with [app backgroundTimeRemaining]
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];