I have a small issue with the startMonitoringSignificantLocationChanges, it doesn't seem to work. the location icon is shown, but my delegate function is never triggered
I have made a class for it here it is:
header file:
#interface geolocation : NSObject < CLLocationManagerDelegate > {
CLLocationManager *locationManager;
}
- (void) significantChange;
- (void) stopSignificantChange;
- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation;
- (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error;
#end
m file:
#implementation geolocation
- (void) significantChange
{
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startMonitoringSignificantLocationChanges];
}
- (void) stopSignificantChange
{
[locationManager stopMonitoringSignificantLocationChanges];
}
- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UILocalNotification* alarm = [[UILocalNotification alloc] init];
if (alarm)
{
alarm.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.alertBody = #"Time to wake up!";
alarm.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:alarm];
}
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
});
}
- (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
}
- (void) dealloc
{
locationManager.delegate = nil;
locationManager = nil;
}
#end
in AppDelegate I call it:
- (void) applicationDidEnterBackground:(UIApplication *)application
{
[UIApplication sharedApplication].applicationIconBadgeNumber = 1;
[geo significantChange];
return [super applicationDidEnterBackground:application];
}
In my case "significant" means 11 callbacks in more than 60 km. So you might just be at the limit.
Edit: That is driving straight south. If you are just driving round and round it is very reasonable that nothing happens.
Related
I have an application that makes the location trace in background. As soon as I get WhatsApp or the phone my location stops and resumes after seeing the image. Would anyone have information on this problem?
Image
- (void) startUpdatingLocationFonction
{
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
[self.locationManager startUpdatingLocation];
self.locationManagerStartDate = [NSDate date];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[AppDelegate sharedInstance].latitude=newLocation.coordinate.latitude;
[AppDelegate sharedInstance].longitude=newLocation.coordinate.longitude;
if (![[FoncGlobal sharedFoncGlobal] isValidLocation:(CLLocation *)newLocation withOldLocation:(CLLocation *)oldLocation])
return;
if ((oldLocation.coordinate.longitude != newLocation.coordinate.longitude)
|| (oldLocation.coordinate.latitude != newLocation.coordinate.latitude))
{
[[FoncGlobal sharedFoncGlobal] upDateTraceUser: [NSString stringWithFormat:#"%.8lf",newLocation.coordinate.latitude]: [NSString stringWithFormat:#"%.8lf",newLocation.coordinate.longitude] :[NSString stringWithFormat:#"%.8lf",newLocation.altitude]:[NSString stringWithFormat:#"%.8lf",newLocation.speed]:0];
}
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (_inBackground) {
[self extendBackgroundRunningTime];
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self extendBackgroundRunningTime];
_inBackground = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
_inBackground = NO;
}
- (void)extendBackgroundRunningTime {
if (bgTask != UIBackgroundTaskInvalid) {
return;
}
NSLog(#"Attempting to extend background running time");
__block Boolean self_terminate = NO;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"DummyTask" expirationHandler:^{
NSLog(#"Background task expired by iOS");
if (self_terminate) {
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (true) {
if ([AppDelegate sharedInstance].startTrack)
[self startUpdatingLocationFonction];
[NSThread sleepForTimeInterval:3];
}
});
}
I am having problems with my app. I am using beacons but they don't seem to enterRegion when my is running in the background. The beacons arre exiting but not entering. They do enter when the app is running in the foreground. Below is my code. Thanks in advance.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:#"BEC26202-A8D8-4A94-80FC-9AC1DE37DAA6"];
NSString *regionIdentifier = #"us.iBeaconModules";
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:regionIdentifier];
NSLog(#"%#",beaconUUID);
[self.locationManager requestAlwaysAuthorization];
self.locationManager.delegate = self;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.locationManager.allowsBackgroundLocationUpdates=YES;
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
[self.locationManager startUpdatingLocation];
self.beaconRegion.notifyOnEntry=YES;
self.beaconRegion.notifyOnExit=YES;
self.beaconRegion.notifyEntryStateOnDisplay=YES;
[self.locationManager startMonitoringForRegion:self.beaconRegion];
return YES;
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
[manager startRangingBeaconsInRegion:(CLBeaconRegion*)region];
[self.locationManager startUpdatingLocation];
CLBeaconRegion *test = (CLBeaconRegion*)region;
NSLog(#"You entered the region. %#", test.minor);
[self sendLocalNotificationWithMessage:#"You enterd the region."];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Are you forgetting something?";
notification.soundName = #"Default";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
CLBeaconRegion *test = (CLBeaconRegion*)region;
NSLog(#"You exited the region.%#", test.minor);
[self sendLocalNotificationWithMessage:#"You exited the region. "];
}
-(void)sendLocalNotificationWithMessage:(NSString*)message {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = message;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
- (void) locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
[self.locationManager requestStateForRegion:self.beaconRegion];
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (state == CLRegionStateInside)
{
//Start Ranging
[manager startRangingBeaconsInRegion:self.beaconRegion];
}
else
{
//Stop Ranging here
}
}
Please add following in your plist
NSLocationAlwaysUsageDescription
NSLocationAlwaysUsageDescription
This application requires location services to work
I have created multiple geo-fence to monitor region entry/exit events.
I have created a location manager in AppDelegate.h file.
#interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property(nonatomic,retain)CLLocationManager *locationManager;
#property(nonatomic,retain)CLLocation *currentLocation;
+(AppDelegate *)sharedDelegate;
AppDelegate.m file
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification) {
NSLog(#"AppDelegate didFinishLaunchingWithOptions");
application.applicationIconBadgeNumber = 0;
}
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
[application registerUserNotificationSettings:settings];
}
else // iOS 7 or earlier
{
UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
[application registerForRemoteNotificationTypes:myTypes];
}
if (!self.locationManager)
{
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
//locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.distanceFilter = 2.0f;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
[self.locationManager requestAlwaysAuthorization];
}
if ([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)])
{
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
if ([self.locationManager respondsToSelector:#selector(pausesLocationUpdatesAutomatically)])
{
self.locationManager.pausesLocationUpdatesAutomatically= NO;
}
[self.locationManager stopMonitoringSignificantLocationChanges];
if ([CLLocationManager locationServicesEnabled] && [CLLocationManager authorizationStatus] != kCLAuthorizationStatusDenied)
{
[self.locationManager startUpdatingLocation];
}
// Override point for customization after application launch.
return YES;
}
-(void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
NSLog(#"Started monitoring %# region",region.identifier);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
NSLog(#"%#",[locations description]);
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
dispatch_async(dispatch_get_main_queue(), ^{
if ([[UIApplication sharedApplication] applicationState]==UIApplicationStateBackground || [[UIApplication sharedApplication] applicationState]==UIApplicationStateInactive)
{
UILocalNotification *localnotification = [[UILocalNotification alloc]init];
localnotification.fireDate=[NSDate dateWithTimeIntervalSinceNow:1];
localnotification.alertBody=#"You are enter in region.";
localnotification.timeZone=[NSTimeZone defaultTimeZone];
localnotification.repeatInterval = 0;
localnotification.hasAction=YES;
[[UIApplication sharedApplication]scheduleLocalNotification:localnotification];
}
else
{
[[[UIAlertView alloc]initWithTitle:#"message" message:#"Enter into region." delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok ", nil] show];
}
});
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
dispatch_async(dispatch_get_main_queue(), ^{
if ([[UIApplication sharedApplication] applicationState]==UIApplicationStateBackground || [[UIApplication sharedApplication] applicationState]==UIApplicationStateInactive)
{
UILocalNotification *localnotificationExit = [[UILocalNotification alloc]init];
localnotificationExit.fireDate=[NSDate dateWithTimeIntervalSinceNow:1];
localnotificationExit.alertBody=#"You are exit from region.";
NSLog(#"Exit from region.");
localnotificationExit.timeZone=[NSTimeZone defaultTimeZone];
localnotificationExit.repeatInterval = 0;
localnotificationExit.hasAction=YES;
[[UIApplication sharedApplication]scheduleLocalNotification:localnotificationExit];
}
else
{
[[[UIAlertView alloc]initWithTitle:#"message" message:#"Exit from region." delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok ", nil] show];
}
});
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
[[[UIAlertView alloc] initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
This things are to manage the region monitoring.
Now my view controller are adding the regions for monitoring.
-(void)AddRegionsInGeoFence
{
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
//----1
CLLocationCoordinate2D centerCoordinate1 = CLLocationCoordinate2DMake(23.046518, 72.543337);
CLCircularRegion *region1 =[[CLCircularRegion alloc] initWithCenter:centerCoordinate1 radius:200 identifier:#"Location First"];
NSLog(#"%#",[region1 description]);
region1.notifyOnEntry=YES;
region1.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region1];
NSLog(#"Started Monitoring- %#", [region1 description]);
}
[self.mapview setShowsUserLocation:YES];
[self.mapview setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
//----2
CLLocationCoordinate2D centercoordinate2=CLLocationCoordinate2DMake(23.064381, 72.531181);
CLCircularRegion *region2=[[CLCircularRegion alloc]initWithCenter:centercoordinate2 radius:200 identifier:#"Location Second"];
NSLog(#"%#",[region2 description]);
region2.notifyOnEntry=YES;
region2.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region2];
NSLog(#"Started Monitoring- %#", [region2 description]);
}
//----3
CLLocationCoordinate2D centercoordinate3=CLLocationCoordinate2DMake(23.083583,72.546441);
CLCircularRegion *region3=[[CLCircularRegion alloc]initWithCenter:centercoordinate3 radius:200 identifier:#"Location Third"];
NSLog(#"%#",[region3 description]);
region3.notifyOnEntry=YES;
region3.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region3];
NSLog(#"Started Monitoring- %#", [region3 description]);
}
//4
CLLocationCoordinate2D centercoordinate4=CLLocationCoordinate2DMake(23.122255, 72.584499);
CLCircularRegion *region4=[[CLCircularRegion alloc]initWithCenter:centercoordinate4 radius:500 identifier:#"Location Fourth"];
NSLog(#"%#",[region4 description]);
region4.notifyOnEntry=YES;
region4.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region4];
NSLog(#"Started Monitoring- %#", [region4 description]);
[standardDefaults setBool:YES forKey:#"EnterRegion"];
[standardDefaults synchronize];
}
}
My Problem is region monitoring methods are called multiple times even if I am not moving in side the region itself. Everything else is working fine, Accuracy buffer is around 50-80 meters that is fine for me.
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
Also if I am turning off Wi-Fi then it's calling up these methods back to back saying exit from region and enter in to region. As far as I know GPS accuracy is depends on Wi-Fi.
Any help would be highly appreciated.
a Possible workaround in the interim while the apple bug gets fixed is to rate limit the callback; thereby not acting on all the callbacks but limiting the rate at which the callbacks can get processed.
Callback execution portions that happen before the time period expires get ignored.
Here is and example code that could assist, not tested:
The rate is limited to 2 seconds.
-(void)methodRateLimit {
#synchronized(self) {
// rate limit begin
static NSDate *lastTimeExit = nil;
if (!lastTimeExit) {
lastTimeExit = [NSDate distantPast]; // way back in time
}
NSDate *now = [NSDate date];
if ([now timeIntervalSinceDate:lastTimeExit] > 2) {
// do work here
NSLog(#"Executing");
lastTimeExit = now;
} else {
NSLog(#"Limiting");
}
}
}
Im building a simple ios app with IBeacon, Im using startMonitoringForRegion for detect beacons.
Thats working ok.
I also want to check the users location when the app is background or closed, for that purpose im using startMonitoringSignificantLocationChanges. In background modes works ok, but when the app is closed the didUpdateLocations: callback is not fired.
My code (from AppDelegate.m) is below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
[self.locationManager startMonitoringSignificantLocationChanges];
NSString *message = #"UIApplicationLaunchOptionsLocationKey";
[self sendLocalNotificationWithMessage:message];
}
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif)
{
NSString *idN = [localNotif.userInfo objectForKey:#"id_notif"];
[self sendToDetail:idN];
}
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]) {
NSLog(#"RESPONDS!!!!!");
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound|UIUserNotificationTypeBadge categories:nil]];
}
[self resetBadge];
// Override point for customization after application launch.
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6A"];
NSString *regionIdentifier = #"iBeacons region 1";
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: beaconUUID identifier: regionIdentifier ];
beaconRegion.notifyEntryStateOnDisplay = YES;
self.locationManager = [[CLLocationManager alloc]init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]){
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager.delegate = self;
//self.locationManager.pausesLocationUpdatesAutomatically = NO;
[self.locationManager startMonitoringForRegion:beaconRegion];
return YES;
}
- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
[manager startRangingBeaconsInRegion:(CLBeaconRegion*) region];
NSLog(#"didEnterRegion");
}
- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
[manager stopRangingBeaconsInRegion:(CLBeaconRegion*) region];
NSLog(#"didExitRegion");
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"startMonitoringSignificantLocationChanges");
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"stopMonitoringSignificantLocationChanges");
[self.locationManager stopMonitoringSignificantLocationChanges];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
NSString *message = #"didUpdateLocations";
[self sendLocalNotificationWithMessage:message];
CLLocation *currentLocation = locations[0];
if (currentLocation != nil) {
NSString *longitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
NSString *latitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
NSString *message = [NSString stringWithFormat:#"longitud: %# latitud: %#", longitude,latitude ];
[self sendLocalNotificationWithMessage:message];
}
}
Any idea?
Thanks
I am trying to send GPS data information to our server about every 5 minutes, no matter if the app is running or if it's in the background. I can get it to run but it seems to run constantly. I set up a timer to send every 10 seconds for testing but it just keeps sending. I don't think it's the timer that is wrong, I believe locationManager isn't stopping and I don't know why.
This is my code
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Went to Background");
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[self.locationManager startUpdatingLocation];
self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self.locationManager selector:#selector(startUpdatingLocation) userInfo:nil repeats:YES];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
[NSTimer scheduledTimerWithTimeInterval:10 target:self.locationManager selector:#selector(startUpdatingLocation) userInfo:nil repeats:YES];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (newLocation.horizontalAccuracy <= 100.0f) {
// Use json and send data to server
...
...
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
self.locationManager.delegate = nil;
}
}
It does the same thing no matter if it's in the background or foreground. Is there something else I need to do to stop locationManager from updating?
To send the location periodically to the server, you want to store and compare a date when updates are received, don't use timers as they are unreliable when the app is in the background.
#implementation
{
NSDate* _lastSentUpdateAt;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Went to Background");
// Update in 5 minutes.
_lastSentUpdateAt = [NSDate date];
[self.locationManager startUpdatingLocation];
}
// ...
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// Accuracy is good & 5 minutes have passed.
if (newLocation.horizontalAccuracy <= 100.0f && [_lastSentUpdateAt timeIntervalSinceNow] < -5 * 60) {
// Set date to now
_lastSentUpdateAt = [NSDate date];
// Use json and send data to server
...
...
}
}
#end