2

I'm having trouble with CoreLocation region delegate methods in iOS 5, both in simulator and on devices. I'm trying to add a region for monitoring, and then waiting for the didStartMonitoring delegate callback. Rarely, it works fine. However, usually neither didStartMonitoring nor monitoringDidFail get called. The region does get added to monitoredRegions. The delegate object is set correctly, and usually gets calls for didEnterRegion and didExitRegion. The location manager is never released. This is on the main thread. I've checked all the conditions I can think of.

-(id) init
{
   self = [super init];
   if( self ) {
      NSLog( @"initializing location manager" );
      self.locationManager = [[CLLocationManager alloc] init];
      locationManager.delegate = self;
      [locationManager startUpdatingLocation];
   }
   return self;
}

-(void) startMonitoringRegion
{
   BOOL monitoring = NO;    
   if ( [CLLocationManager regionMonitoringAvailable] ) {
      if ( [CLLocationManager regionMonitoringEnabled] ) {
         if( [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized ) {
            monitoring = YES;
         } else {
            NSLog( @"app is not authorized for location monitoring" );
         }
      } else {
         NSLog( @"region monitoring is not enabled" );
      }
   } else {
      NSLog( @"region monitoring is not available" );
   }
   if( !monitoring ) return;

   CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:locationManager.location.coordinate 
                                                          radius:50
                                                      identifier:@"majorRegion"];
   NSLog( @"trying to start monitoring for region %@", region );
   [locationManager startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyBest];
}

-(void)     locationManager:(CLLocationManager*)manager 
didStartMonitoringForRegion:(CLRegion*)region
{
   NSLog( @"region monitoring started" );
}

- (void)   locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region 
                 withError:(NSError *)error
{
   NSLog( @"region monitoring failed" );
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
   NSLog( @"location manager failed" );
}

Anybody have any ideas? I can handle this, but it appears that the didEnterRegion and didExitRegion delegate methods are also sometimes inconsistent, and that's a big problem for me.

Edit: I can replicate this functionality within a single app delegate -- no custom objects or anything. See implementation below. The regions get added (seen when printed), but didStartMonitoringRegion never gets called.

@implementation AppDelegate

@synthesize window = _window;
@synthesize locationManager;

-(void) startMonitoringRegion
{
   BOOL monitoring = NO;    
   if ( [CLLocationManager regionMonitoringAvailable] ) {
      if ( [CLLocationManager regionMonitoringEnabled] ) {
         if( [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized )  {
            monitoring = YES;
         } else {
            NSLog( @"app is not authorized for location monitoring" );
         }
      } else {
         NSLog( @"region monitoring is not enabled" );
      }
   } else {
      NSLog( @"region monitoring is not available" );
   }
   if( !monitoring ) return;

   CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:locationManager.location.coordinate 
                                                              radius:50.
                                                          identifier:@"majorRegion"];
   NSLog( @"trying to start monitoring for region %@", region );
   [locationManager startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyBest];
}

-(void) printMonitoredRegions
{
   NSLog( @"printing regions:" );
   for( CLRegion* region in locationManager.monitoredRegions )
      NSLog( @"%@", region );
}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   NSLog( @"initializing location manager" );
   self.locationManager = [[CLLocationManager alloc] init];
   locationManager.delegate = self;
   [locationManager startUpdatingLocation];

   [self startMonitoringRegion];
   [self performSelector:@selector(printMonitoredRegions) withObject:nil afterDelay:2.];

   return YES;
}


- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation
{
   //NSLog( @"location updated" );
}

-(void)     locationManager:(CLLocationManager*)manager 
didStartMonitoringForRegion:(CLRegion*)region
{
   NSLog( @"region monitoring started" );
}

-(void) locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion*)region
{
   NSLog( @"did enter region" );
}

-(void) locationManager:(CLLocationManager*)manager didExitRegion:(CLRegion*)region
{
   NSLog( @"did exit region" );
}

- (void)   locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region 
                 withError:(NSError *)error
{
   NSLog( @"region monitoring failed" );
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
   NSLog( @"location manager failed" );
}

@end

Log:

2012-02-21 10:53:50.397 locationtest[64440:f803] initializing location manager
2012-02-21 10:53:50.412 locationtest[64440:f803] trying to start monitoring for region (identifier majorRegion) <LAT,LONG> radius 50.00m
2012-02-21 10:53:52.414 locationtest[64440:f803] printing regions:
2012-02-21 10:53:52.416 locationtest[64440:f803] (identifier majorRegion <LAT,LONG> radius 50.00m

Edit 2: I just noticed that the iOS implementation of the CLLocationManagerDelegate protocol differs slightly from the Mac implementation -- notably, Mac doesn't have didStartMonitoringRegion. Could there be any way I'm accidentally using the Mac libraries instead of the iOS libraries?

Community
  • 1
  • 1
jab
  • 4,053
  • 3
  • 33
  • 40

3 Answers3

3

See what Apple says:

http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html

Testing Your App’s Region Monitoring Support

When testing your region monitoring code in iOS Simulator or on a device, realize that region events may not happen immediately after a region boundary is crossed. To prevent spurious notifications, iOS does not deliver region notifications until certain threshold conditions are met. Specifically, the user’s location must cross the region boundary and move away from that boundary by a minimum distance and remain at that minimum distance for at least 20 seconds before the notifications are reported.

The specific threshold distances are determined by the hardware and the location technologies that are currently available. For example, if Wi-Fi is disabled, region monitoring is significantly less accurate. However, for testing purposes, you can assume that the minimum distance is approximately 200 meters.

Khawar
  • 9,151
  • 9
  • 46
  • 67
  • This is a good note, but it's not the issue. The `didEnterRegion` and `didExitRegion` delegate methods are called (eventually, as you say). But `didStartMonitoringForRegion` never gets called even though the location manager is clearly monitoring that region. – jab Apr 11 '13 at 19:20
  • same problem here, as described in above comment, did u find ny solution? – nidIOS May 30 '14 at 14:09
0

For anyone else looking for an answer: Monitoring for a region has to be explicitly stopped. Either before the app terminates or manually (to make it really obvious) (thats because it actually runs in the background)

If you saw didStartMonitoringFor being called but then you don't see it anymore its because you are already monitoring (even if you closed the app)

Otto
  • 16
  • 1
  • I haven't actually tested this, but it seems so likely that I'm going to accept this answer. :) – jab Sep 26 '18 at 18:21
0

Your region radius is so small, there is no chance you will get an accurate pickup. Try expanding it from half a meter, to something like 10 or 20 and work from there.

You could also try setting the accuracy as well, but the radius is your main issue I'm almost certain.

Update

I just noticed you are calling -startUpdatingLocatuon rather than -startMonitoringRegion. Try including your code where you build your CLRegion and add it to be monitored.

Bill Burgess
  • 14,054
  • 6
  • 49
  • 86
  • Sorry, I realized that and fixed it after I posted. It didn't help. – jab Feb 20 '12 at 22:06
  • Anyway, the problem I'm asking about isn't detecting region entry/exit, it's why the `didStartMonitoringForRegion` delegate method is never called (never anymore, though I'd swear I saw it work once or twice). – jab Feb 20 '12 at 22:10
  • Re your update: I've done it several ways, but in this implementation, `startMonitoringRegion` is called immediately after the object is initialized (right after the `init` method completes). Adding a delay there doesn't help. – jab Feb 21 '12 at 18:45