5
votes

I have been developing region monitoring on iOS for about 2 months. Recently we have found a glitch where all the regions within a certain radius (about 4.7KM or 4700 meters) triggered at the same time. The current location of the device is not even close to any of the regions. I am not sure what could have triggered that event. I have searched through StackOverFlow, Apple Developer forums and etc, I haven't find any similar issue with what I am facing.

In the app that I am developing, we are monitoring 8 regions within the City (Kuala Lumpur). On one instance, my colleague found out that there were 4 regions notification triggered on his phone at the same time. Below is the map showing his location, all the monitored regions, the potential radius that triggered the 4 region notifications.

iOS region Monitoring

  • The green marker is the location of the device when receiving the notification.
  • The blue circle is the potential radius of the device (about 4700 meters) which is covering 4 regions that send the notification to the device.
  • The red circle is the radius for each of the region.
  • There are 2 other regions on the map which never send notifications (never cover under blue circle)

Screen shot of the triggered notifications:

iOS region monitoring glitch

Here is my code for location Manager:-

CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = kCLDistanceFilterNone;

Here is my code for didEnterRegion:-

-(void)locationManager:(CLLocationManager *)manager 
        didEnterRegion:(CLRegion *)region{

NSString* message = [NSString stringWithFormat:@"Message"];        
UIApplicationState state = [[UIApplication sharedApplication] applicationState];

if (state == UIApplicationStateBackground || state == UIApplicationStateInactive)
{
   UILocalNotification *notification = [[UILocalNotification alloc] init];
   notification.fireDate = [NSDate date];
   NSTimeZone* timezone = [NSTimeZone defaultTimeZone];
   notification.timeZone = timezone;
   notification.alertBody = message;
   notification.alertAction = @"Show";
   notification.soundName = UILocalNotificationDefaultSoundName;
   [[UIApplication sharedApplication] scheduleLocalNotification:notification];
 }

}

Note: This issue does not happen every time, it only happens once in a while. The 4700 meters is the radius that I came out with after analysing the location of the 4 triggered regions. I am not sure if this is a glitch on my code, on the iOS or there is a problem on the local telco in my country. In the latest version of the app, I am adjusting the distanceFiter to 10 and we are testing it right now to see if this will solve the issue.

//locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.distanceFilter = 10;

The method didEnterRegion never return the location of the user, I am not able to filter out the potential bad location with a big radius like the example I show above. What I can do to solve this glitch?

Any developer who is facing the similar issue, please come forward and share your experience and solution to solve this issue (if there is any). Thanks.

3
Are you sure your app is not being killed in the background and then started later by iOS (7) therefore causing the check to occur for more than 1 region?fumoboy007
I am very sure that the app is not killed. Even if it is killed, how can a device location appears on so many different regions at the same time? The current location of the device is not even close to any of the 4 regions that I mentioned above. The closest region from the location of the device is 3000 meters away.Ricky
Hi,i too facing the same problem, are you able to fix the issue, and on which version of device you are getting this issue.. please check my issue here.. stackoverflow.com/questions/25841870/…nidIOS

3 Answers

6
votes

I have found a fix for this strange bug. We have tested for over 1 week and so far we haven't see the same bug again. Here is the solution:-

-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{

NSLog(@"didEnterRegion");
CLLocation * lastLocation = [manager location];

BOOL doesItContainMyPoint;

if(lastLocation==nil)
    doesItContainMyPoint = NO;
else{
    CLLocationCoordinate2D theLocationCoordinate = lastLocation.coordinate;
    CLCircularRegion * theRegion = (CLCircularRegion*)region;
    doesItContainMyPoint = [theRegion containsCoordinate:theLocationCoordinate];
}

if(doesItContainMyPoint){
    NSString* message = [NSString stringWithFormat:@"You are now in this region:%@",region.identifier];
    UIApplicationState state = [[UIApplication sharedApplication] applicationState];

    if (state == UIApplicationStateBackground || state == UIApplicationStateInactive)
       {
         UILocalNotification *notification = [[UILocalNotification alloc] init];
         notification.fireDate = [NSDate date];
         NSTimeZone* timezone = [NSTimeZone defaultTimeZone];
         notification.timeZone = timezone;
         notification.alertBody = message;
         notification.alertAction = @"Show";
         notification.soundName = UILocalNotificationDefaultSoundName;
         [[UIApplication sharedApplication] scheduleLocalNotification:notification];
        }
  }
}

I use CLLocation * lastLocation = [manager location]; to get the latest location from the device and use this coordinate to see if it is inside the triggered region with containsCoordinate: method. If it is inside, then only the local notification will trigger.

For the details explanation on this bug and the way to fix it, you may visit: iOS Region Monitoring and Location Manager

3
votes

HERE is full proof working solution/fix for iOS geo-fencing glitch !

It will both solve problem of receiving wrong multiple events on network switch like wifi to mobile data and vice versa..also it gives you accurate results/notifications with this logic/fix

Basically, LocationManager should be taken as singleton and distanceFilter should be kCLDistanceFilterNone and desiredAccuracy should be kCLLocationAccuracyBestForNavigation (We have taken this for best possible accuracy as we need real background monitoring of location very accurate for real time geofence entry notifications to send to users)

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{    

CLLocation * lastLocation = [manager location];

BOOL doesItContainMyPoint;


if(lastLocation==nil)
    doesItContainMyPoint = NO;
else{
    CLLocationCoordinate2D theLocationCoordinate = lastLocation.coordinate;
    CLCircularRegion * theRegion = (CLCircularRegion*)region;

//we need to take new instance of given region for which the iOS triggers entry event..Adding 50.0 meters is needed just to make sure that containsCoordinate method of CLCircularRegion works well.Because there is lag/difference measured in our real time field testing that brought us to this conclusion and it works like a charm.If we do not add this 50.0 meters in the fence for which we are getting this event then there is a chance that containsCoordinate might miss the recent point/coordinate to consider in given region...

    CLCircularRegion * theCircularRegion = [[CLCircularRegion alloc]initWithCenter:theRegion.center radius:theRegion.radius+50.0 identifier:theRegion.identifier];
    doesItContainMyPoint = [theCircularRegion containsCoordinate:theLocationCoordinate];
}

if(doesItContainMyPoint){
    NSLog(@"ItContainMyPoint");
//trigger local notification...

}else{
    NSLog(@"ItDoesNotContainMyPoint");
    //do not trigger local notification...because it is triggered due to switching network(wifi to mobile data and vice versa) Currently user is not at all in the region for which we are getting event of entry
    return;
}

}
1
votes

It's an old question, but let my share my experience with Region Monitoring concerning inaccurate didExit- didEnterRegion calls:

One thing to note is that if you have Wifi turned off on the device, the reliability of geofence on iOS devices deteriorates a lot.

In the case of the asker, you should't just check the .coordinate of the last location to debug, but also the .horizontalAccuracy of the CLLocation. That might reveal a very low accuracy (big possible area) if wifi and GPS is turned off. In the case that you also run GPS location tracking, the accepted answer is actually quite a good answer. If not, the .horizontalAccuracy might be a very large number. Region Monitoring does not seem (in my experience) to listen to GPS updates, so you can't enhance its reliability that way. But having WiFi enabled can. In an urban area that is.

Without Wifi and a GPS trick like accepted answer uses, all the device has is cell tower location. I have found that didExitRegion and didEnterRegion may be unreliable if all the device has to get it's location, are cell towers. This is because the accuracy of cell tower location is of course much lower than wifi. In that case, the low accuracy (big possible area) may mean it's in any of the four regions of the asker. It may even be called multiple times for a single region because it may suddenly think (based on cell towers) that it's a kilometer away, then realise it's not, which can cause didEnterRegion to be called multiple times.

The same goes for didExitRegion. If the accuracy is really low, the system cannot be sure you left the region until several kilometers away (which is one of the commenters's problem)

So if you are not sure your users are using your app having wifi always turned on (which you can't check or assure on iOS), make sure you don't make the regions too small or use the accepted answer with GPS ([locationManager startUpdatingLocation]) to ensure that you're really in that region. In that case, also check that the location is accurate and young enough. Something like:

CLLocation * lastLocation = [manager location];
NSTimeInterval locationAge = -[lastLocation.timestamp timeIntervalSinceNow];

if (lastLocation != nil && locationAge < MAX_AGE && self.currentLocation.horizontalAccuracy <= MAX_ACCURACY) {
    CLLocationCoordinate2D theLocationCoordinate = lastLocation.coordinate;
    CLCircularRegion * theRegion = (CLCircularRegion*)region;
    doesItContainMyPoint = [theRegion containsCoordinate:theLocationCoordinate];
}

But if there is any way you can convince the user to always have Wifi enabled when using the app, do!! It makes it so much more reliable.