In My Application.
I used following code to Update Location Coordinates to Server with 1 min time Interval. Its working fine for some times 10 to 20 Hours. but its some time stopped Periodically Please help me.
UIApplication *application1 = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier background_task;
background_task = [application1 beginBackgroundTaskWithExpirationHandler:^ {
[application1 endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//### background task starts
//NSLog(#"Running in the background\n");
while(TRUE)
{
[locationManager startMonitoringSignificantLocationChanges];
[locationManager startUpdatingLocation];
NSUserDefaults *addValue=[NSUserDefaults standardUserDefaults];
NSString *oldLat=[addValue stringForKey:#"OLD_LAT"];
NSString *oldLong=[addValue stringForKey:#"OLD_LONG"];
CLLocationManager *manager = [[CLLocationManager alloc] init];
manager.desiredAccuracy=kCLLocationAccuracyBestForNavigation;
NSString *locLat = [NSString stringWithFormat:#"%lf",manager.location.coordinate.latitude];
NSString * locLong = [NSString stringWithFormat:#"%lf",manager.location.coordinate.longitude];
float lat_new=[locLat floatValue];
float lang_new=[locLong floatValue];
float lat_old=[oldLat floatValue];
float lang_old=[oldLong floatValue];
if (lat_new>0 && lang_new>0) {
//NSLog(#"location changed");
[addValue setObject:locLat forKey:#"OLD_LAT"];
[addValue setObject:locLong forKey:#"OLD_LONG"];
float from_lat_value=[locLat floatValue];
float from_long_value=[locLong floatValue];
locLat=[NSString stringWithFormat:#"%f",from_lat_value];
locLong=[NSString stringWithFormat:#"%f",from_long_value];
//NSLog(#"LST:%#,%#",locLat,locLong);
NSUserDefaults *addLat=[NSUserDefaults standardUserDefaults];
[addLat setObject:locLat forKey:#"FROM_LAT"];
[addLat setObject:locLong forKey:#"FROM_LONG"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/user/insertUserLocation",CONFIG_BASE_URL]];
// //NSLog(#"URL:%#",url);
__block ASIFormDataRequest *requestmethod = [ASIFormDataRequest requestWithURL:url];
NSUserDefaults *userValue=[NSUserDefaults standardUserDefaults];
NSString *deviceToken=[NSString stringWithFormat:#"%#",[userValue objectForKey:#"DEVICE_TOKEN"]];
NSString *loginValidation=[userValue objectForKey:#"USER_ID"];
[requestmethod setValidatesSecureCertificate:NO];
[requestmethod setPostValue:deviceToken forKey:#"deviceToken"];
[requestmethod setPostValue:locLat forKey:#"latitude"];
[requestmethod setPostValue:locLong forKey:#"longitude"];
[requestmethod setPostValue:loginValidation forKey:#"userID"];
[requestmethod setTimeOutSeconds:180];
[requestmethod setCompletionBlock:^{
NSString *responseString23 = [requestmethod responseString];
//NSLog(#"BACKGROUND RESPONCE:%#",responseString23);
}];
[requestmethod setFailedBlock:^{
NSError *error = [requestmethod error];
if ([[NSString stringWithFormat:#"%#",error.localizedDescription] isEqualToString:#"The request timed out"]||[[NSString stringWithFormat:#"%#",error.localizedDescription] isEqualToString:#"Please connect online to use the app"])
{
}
else
{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Alert-360"
message:[NSString stringWithFormat:#"%#",error.localizedDescription] delegate:self
cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
// [self endBackgroundUpdateTask];
}];
[requestmethod startAsynchronous];
}
[locationManager stopMonitoringSignificantLocationChanges];
[locationManager stopUpdatingLocation];
}
[NSThread sleepForTimeInterval:BACKGROUND_INTERVAL_CHECKIN]; //wait for 1 sec
//Clean up code. Tell the system that we are done.
[application1 endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
//NSLog(#"background Task finished");
});
Check my commends.
After App Enters background, we can get Latitude and longitude values.but we can't post this data to server. Apple restricted this.
I calculated both Foreground and Background time remaining code, I got following results
Foreground:
Foreground time remaining >>>>: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000 seconds (2147483647 mins)
Background:
Background time remaining >>>>: 159.223682 seconds (2 mins)
For my final commend: we can't run background location update to server long time.
I used to receive the same crashes in my gps trackers that run in the background. I fixed those crashes by checking the values validity before every call. I found some of my variables are nil. Calling selectors of null variables will most probably kick the execution outside the task function with no rescheduling.
Guarding my calls with a null check on every task call fixed my issue. You can also add as much logs as you can; and leave it running for a while and then check where the crash happen by checking your logs. Beside the normal nslog output I used to have an external shared document logging text file as well. You should be able to have all your application logs in that file separated at any time in the future. Make sure to turn that logging off before sending to market.
Related
I'm trying to send notifications in objective c but I can't figure out how.
So far I have
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if(self.username != nil)
{
NSLog(#"Background fetch username: %#", self.username);
[self fetchNotifications];
}
completionHandler(UIBackgroundFetchResultNewData);
}
- (void)fetchNotifications
{
NSURL *url = [NSURL URLWithString:
[NSString stringWithFormat:#"%#%#",
#"https://myurl?username="
, self.username]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary *notifications = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
int nbMessages = [[notifications objectForKey:#"NbOfUnreadMessage"] intValue];
int nbOfNewMatch = [[notifications objectForKey:#"NbOfNewMatch"] intValue];
int nbOfNewPlanification = [[notifications objectForKey:#"NbOfNewPlanification"] intValue];
//int nbMessages = [[notifications objectForKey:#"NbOfUnreadMessage"] intValue];
NSLog(#"Notifications - nbMessage: %i",nbMessages);
NSLog(#"Notifications - nbNewMatch: %i",nbOfNewMatch);
NSLog(#"Notifications - nbNewPlan: %i",nbOfNewPlanification);
// Schedule the notification
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = nil;
localNotification.alertBody = #"test";
localNotification.alertAction = #"Show me the item";
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}];
}
When I try on the iPhone 6 simulator with IOS 8.1 I have the following error
Attempting to badge the application icon but haven't received permission from the user to badge the application
I will have to read and understand this post
but my real problem is when I use an iPhone 5 IOS 7.1 simulator, the function fetchNotifications is not even called.
Why fetchNotifications is only called under IOS 8.1?
performFetchWithCompletionHandler is called under both OS
Attempting to badge the application icon but haven't received permission from the user to badge the application
is the way Xcode tells you that you can't change the notifications badge value while running on the simulator. It's not allowed
This delegate method is called when iOS decide. From the UIApplicationDelegate documentation:
When an opportunity arises to download data, the system calls this method to give your app a chance to download any data it needs. More importantly, the system uses the elapsed time to calculate power usage and data costs for your app’s background downloads. 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.
I have an app that posts data to a server. This data is only posted if the user is within a certain distance to a location. I regisered for background location updates in hope that would allow me to keep the background going. But after about 17-18 minutes, the background stops executing.
I thought it might have been because of the locationManager.pauseslocationupdatesautomatically. But even when I set that to false, the app still terminates at around 17 minutes. Here is the code for my app delegate.
//
// BAAppDelegate.m
// Beacon App
//
// Created by Huy Ly on 2/10/13.
// Copyright (c) 2013 Placesign. All rights reserved.
//
#import "BAAppDelegate.h"
#implementation BAAppDelegate
#synthesize backgroundAnnouncementRevision, backgroundAnnouncementText, backgroundOfferDescription, backgroundOfferName, backgroundOfferPrice, backgroundOfferRevision, isAnnouncing, isOffering, locationManager, targetLocation, currentLocation, beaconTimer;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"Application will resign active");
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Application entered background");
// 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.
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager setPausesLocationUpdatesAutomatically:YES];
[locationManager startUpdatingLocation];
NSLog(#"Starting timer for posting in background");
/*
//Runs the Timer on a background task main thread
UIApplication *app = [UIApplication sharedApplication];
//create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];*/
//and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//run function methodRunAfterBackground
beaconTimer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(sendBeacon) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:beaconTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Application entered foreground");
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
[beaconTimer invalidate];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"Application became active");
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[beaconTimer invalidate];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#"Application will terminate");
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
[beaconTimer invalidate];
}
-(void) sendBeacon{
NSLog(#"Beacon Background Send Started");
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if ([[NSUserDefaults standardUserDefaults]valueForKey:#"PlaceID"]== nil) {
isAnnouncing = FALSE;
isOffering = FALSE;
}
//Do a single distance check to see if user is still within bounds
//Get the current location
[locationManager startUpdatingLocation];
//Compare current location with target location
CLLocationDistance distance = [currentLocation distanceFromLocation:targetLocation];
distance=distance/1000;
//If user is within location boundary, posts to server
if (distance < 0.1 || true) {
NSLog(#"Background Sent");
//Set Up the NSURL
NSString *urlString = [standardUserDefaults valueForKey:#"statusUpdate"];
NSURL *url = [NSURL URLWithString:urlString];
NSString *jsonString = [[NSString alloc] initWithFormat:#"{\"Announcement\":{\"Text\":\"%#\",\"ElementContext\":{\"Revision\":%#,\"Source\":{\"ID\":0,\"Type\":0}}},\"Offer\":{\"Description\":\"%#\",\"ElementContext\":{\"Revision\":%#,\"Source\":{\"ID\":0,\"Type\":0}},\"Name\":\"%#\",\"Price\":%#},\"OpStatus\":{\"ElementContext\":{\"Revision\":0,\"Source\":{\"ID\":0,\"Type\":0}},\"Value\":0},\"PlaceID\":%#,\"ResourcesOnPremise\":[{\"ElementContext\":{\"Revision\":0,\"Source\":{\"ID\":%#,\"Type\":1}},\"OnPremiseStatus\":2,\"Resource\":{\"ID\":%#,\"Type\":1}}],\"SignalSources\":[{\"LastSignal\":0,\"Source\":{\"ID\":0,\"Type\":0}}]}", backgroundAnnouncementText, backgroundAnnouncementRevision, backgroundOfferDescription, backgroundOfferRevision, backgroundOfferName, backgroundOfferPrice, [[NSUserDefaults standardUserDefaults]valueForKey:#"PlaceID"], [[NSUserDefaults standardUserDefaults]valueForKey:#"UserID"], [[NSUserDefaults standardUserDefaults]valueForKey:#"UserID"]];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
//setup the request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [jsonString length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:jsonData];
NSURLConnection *requestConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[requestConnection start];
}
else{
}
}
-(void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(#"Location Manager failed with error %#", error);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
currentLocation = [locations lastObject];
NSLog(#"Manager did update location");
}
-(void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager{
NSLog(#"Location Manager Paused");
}
-(void)locationManagerDidResumeLocationUpdates:(CLLocationManager *)manager{
NSLog(#"Location Manager Resumed");
}
#end
You shouldn't expect to run indefinitely. If you're a location app, then you should expect to be called when the device moves. But if the device isn't moving, then there's no reason for the OS to call you, and it won't.
You need to design your app so that it uses the minimum battery to achieve the user-desired behavior. To that end, if you have a boundary that you care about, you should set up a location region, and you will be woken up whenever the device moves into or out of that region. This is much, much cheaper than constantly watching the GPS.
If the user wants you to record every small movement, then you can set the location manager as you have (with kCLLocationAccuracyBest), but you still will only get called when the device moves. Since this will cause the maximum battery drain, make sure it's the only way to achieve the user's goal.
There is, by design, no way to request "Indefinite Background Time."
In didFinishLaunchingWithOptions a timer loop calling a function httpRequest every 1 minute interval.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//rest of code
NSTimer *notifyTimer = [NSTimer timerWithTimeInterval:60 target:self selector:#selector(httpRequest) userInfo:nil repeats:YES];//7200.0
[[NSRunLoop mainRunLoop] addTimer:notifyTimer forMode:NSDefaultRunLoopMode];
return YES;
}
After pressing home button application is going to background and calling function applicationDidEnterBackground so a background task is starting.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier bgTask;
UIApplication *app = [UIApplication sharedApplication];
expirationHandler = ^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
};
bgTask = UIBackgroundTaskInvalid;
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
}
By httpRequest function I am geting Y from web server after every 1 minute interval so a UILocalNotification fires after every seconds.
-(NSString *)httpRequest {
NSURL *url = [NSURL URLWithString:#"http://192.168.10.67/t.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *userAgent = [NSString stringWithFormat:#"bgTaskTest-IOS"];
[request setValue:userAgent forHTTPHeaderField:#"User-Agent"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPMethod:#"GET"];
[request setTimeoutInterval:25];
NSURLResponse *response;
NSData *dataReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *stringReply = [[NSString alloc] initWithData:dataReply encoding:NSASCIIStringEncoding];
if ([stringReply isEqualToString:#"Y"]) {
[self showLocalNotification:nil]; //calling UILocalNotification
} else {
NSLog(#"%#",stringReply);
}
return stringReply;
}
Function showLocalNotification is calling after every 1 minute based on response of httpRequest function.
-(void)showLocalNotification {
NSString *msg = #"test message";
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *_localNotification = [[UILocalNotification alloc]init];
_localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
_localNotification.timeZone = [NSTimeZone defaultTimeZone];
_localNotification.alertBody = msg;
_localNotification.soundName = UILocalNotificationDefaultSoundName;
_localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber]+1;
[[UIApplication sharedApplication] scheduleLocalNotification:_localNotification];
//[[UIApplication sharedApplication] presentLocalNotificationNow:_localNotification];
}
Everything is right, notification prompts after every 1 munite when application is in background.
But my problem is Background Task's life time is 10 mins, so after 10 mins no notification prompts. For this reason I am starting Background task again in beginBackgroundTaskWithExpirationHandler but my application kill at this time of restarting background task.
I couldn't able to use notification more than 10 mins when application is in background.
Please anybody help me.
There is no way (within the app store guidelines) to run arbitrary code in the background for longer than ten minutes (as you have noticed).
After 10 minutes your app will be suspended. There is a couple of ways around this, registering for other background modes (such as background audio, playing a silent sound file continuously) or background voip or background location services.
These hacky work around will keep your application unsuspended however your application will not get approved for the store.
in iOS7 there are advances to running code in the background, however nothing that will do what you want.
So if this is an app for your own use, use private API's or the method I suggested above, however if you want to get this app on the store, I'm afraid your out of luck.
I am writing an iOS app for scanning barcodes at participating retail locations, where that location would donate to charity on the customer's behalf after the customer scans a QRcode printed on the receipt.
I would like to send a local notification to a user if they are in a participating location for 60 seconds or longer, reminding them to scan any receipts they might get from purchases they made there.
My issue is that I would like to delay a call for 60 seconds when a user enters a region - if after those 60 seconds they are still in that region fire off the local notification - however, sometimes that call to sendLocalNotification within stillInRegion doesn't fire until the app returns to the foreground. I believe this has to do with the thread sometimes ending before the delay is up, but I am not sure. I have tried about every approach I could find on stackoverflow and elsewhere (blocks, nstimers, etc.) but to no avail. Any ideas about how to better approach this problem?
- (void) sendLocalNotification:(NSString *)regionId {
NSLog(#"we entered %# and we're currently in %#", regionId, self.currentRegionId);
if ([regionId isEqualToString:self.currentRegionId]) {// if we're still in the region, send a local notification
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil) return;
NSDate *fireTime = [[NSDate date] addTimeInterval:2]; // adds 2 secs
localNotif.fireDate = fireTime;
localNotif.alertBody = [NSString stringWithFormat:#"Did you just visit %#? If so, don't forget to scan your receipt!", regionId];
localNotif.applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber+1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
}
}
- (void) stillInRegion:(CLRegion *)region {
NSLog(#"did enter region: %#", region.identifier);
[self performSelector:#selector(sendLocalNotification:) withObject:region.identifier afterDelay:60];
}
- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
if (self.didLaunchForRegionUpdate) {
NSString *path = [DGGeofencingHelper applicationDocumentsDirectory];
NSString *finalPath = [path stringByAppendingPathComponent:#"notifications.dg"];
NSMutableArray *updates = [NSMutableArray arrayWithContentsOfFile:finalPath];
if (!updates) {
updates = [NSMutableArray array];
}
NSMutableDictionary *update = [NSMutableDictionary dictionary];
[update setObject:region.identifier forKey:#"fid"];
[update setObject:[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] forKey:#"timestamp"];
[update setObject:#"enter" forKey:#"status"];
[updates addObject:update];
[updates writeToFile:finalPath atomically:YES];
} else {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:#"enter" forKey:#"status"];
[dict setObject:region.identifier forKey:#"fid"];
NSString *jsStatement = [NSString stringWithFormat:#"DGGeofencing.regionMonitorUpdate(%#);", [dict JSONString]];
[self.webView stringByEvaluatingJavaScriptFromString:jsStatement];
}
self.currentRegionId = region.identifier;
self.cRegionEnterTime =[NSDate date];
[self stillInRegion:region];
}
Are the locationManager:didEnterRegion being called in foreground and then your application goes to background?
I'm not very clear about when you are calling those methods, but you could try creating a background task as follows:
Add a property of type NSUInteger called, for example, bgTaskIdentifier, to store your background task identifier.
Before your call to [self stillInRegion:region]; add the following code:
bgTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}];
It should call your stillInRegion method, and continues even if you go to the background, so the delay should continue counting on!. Finally, you should end your background task. To do this, add the following line at the end of your sendLocalNotification method, after the if block:
[[UIApplication sharedApplication] endBackgroundTask:bgTaskIdentifier];
Just let us know if that was helpful! and excuse me for my poor english!
Have a good day!
inside my didEnterRegion method I used to following code to achieve the desired result:
UIApplication* app = [UIApplication sharedApplication];
bgTaskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTaskIdentifier];
bgTaskIdentifier = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:180 target:self selector:#selector(stillInRegion:) userInfo:region.identifier repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
Pretty close to what #lucaslt89 had but a slight variation on it.
My app got rejected by Apple three times, all with the same rejection letter, which is:
We found that your app uses a background mode but does not include
functionality that requires that mode to run persistently. This
behavior is not in compliance with the App Store Review Guidelines.
We noticed your app declares support for location in the
UIBackgroundModes key in your Info.plist but does not include features
that require persistent location.
It would be appropriate to add features that require location updates
while the app is in the background or remove the "location" setting
from the UIBackgroundModes key.
If you choose to add features that use the Location Background Mode,
please include the following battery use disclaimer in your
Application Description:
"Continued use of GPS running in the background can dramatically
decrease battery life."
For information on background modes, please refer to the section
"Executing Code in the Background" in the iOS Reference Library.
Now, as far as I know I am running on the background and "doing something"...
In my AppDelegate I have the following code in didFinishLaunchingWithOptions:
if ([[launchOptions allKeys] containsObject:UIApplicationLaunchOptionsLocationKey] &&
([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]))
{
id locationInBackground = [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey];
if ([locationInBackground isKindOfClass:[CLLocation class]])
{
[self updateMyLocationToServer:locationInBackground];
}
else
{
//Keep updating location if significant changes
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
self.bgLocationManager = locationManager;
self.bgLocationManager.delegate = self;
self.bgLocationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
[bgLocationManager startMonitoringSignificantLocationChanges];
}
}
The AppDelegate also starts a location manager and makes himself the delegate.
Then, I have the following code for handling the location updates on the background:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[self updateMyLocationToServer:newLocation];
}
-(void)updateMyLocationToServer:(CLLocation*)myNewLocation
{
// NSLog(#"Updating Location from the background");
NSString *fbID = [NSString stringWithString:[facebookDetails objectForKey:#"fbID"]];
NSString *firstName = [NSString stringWithString:[facebookDetails objectForKey:#"firstName"]];
NSString *lastName = [NSString stringWithString:[facebookDetails objectForKey:#"lastName"]];
NSString *urlString = [NSString stringWithFormat:#"MY_SERVER_API", fbID, myNewLocation.coordinate.latitude, myNewLocation.coordinate.longitude, firstName, lastName];
NSURL *url = [NSURL URLWithString:urlString];
__block ASIHTTPRequest *newRequest = [ASIHTTPRequest requestWithURL:url];
[newRequest setCompletionBlock:^{
}];
[newRequest setFailedBlock:^{
}];
// [newRequest setDelegate:self];
[newRequest startAsynchronous];
}
I also put a disclaimer in my app description page:
Intensive use of GPS running in the background can dramatically decrease battery life. For this reason, MY_APP_NAME runs on the background just listening for significant location changes.
Is there anything I'm missing here?
This question is old and already answered but you dont need the UIBackgroundModes key if you collect locations using the startMonitoringSignificantLocationChanges API
In locationManager:didUpdateToLocation:fromLocation: or in updateMyLocationToServer: You should check if application is in background state by eg.
[UIApplication sharedApplication].applicationState == UIApplicationStateBackground
And then if Your app is in background mode You should use eg.
backgroundTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
}];
/*Write Your internet request code here*/
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
bgTask = UIBackgroundTaskInvalid;
}
This way application should perform this task completely.
startMonitoringSignificantLocationChanges don't require a background mode registration. Only continuous location changes do.