Reputation: 2645
I'm trying to setup a small geofence (of 100 meters) to alert a user whenever they leave their home. To do this I request the users current location like so:
- (void)requestUsersCurrentLocation
{
if (self.locationManager == nil) self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 100;
[self.locationManager startUpdatingLocation];
}
Using CLLocationManager's delegate methods I check whether the application was able to determine the users current location. In my testing, this works correctly and the application proceeds to call my attemptToRegisterGeofenceAlertForLocation:
method
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
NSDate *eventDate = location.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
[self.locationManager stopUpdatingLocation];
if([self attemptToRegisterGeofenceAlertForLocation:location])
{
[[NSNotificationCenter defaultCenter] postNotificationName:kGeofenceSetupSuccess object:nil];
}
else
{
[[NSNotificationCenter defaultCenter] postNotificationName:kGeofenceSetupFailure object:nil];
}
}
}
So far so good. Here's my custom function to register a relatively small geofence around the users current location:
- (BOOL)attemptToRegisterGeofenceAlertForLocation:(CLLocation *)location
{
// Do not create regions if support is unavailable or disabled
if (![CLLocationManager regionMonitoringAvailable]) return NO;
// Check the authorization status
if (([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorized) && ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusNotDetermined)) return NO;
// Clear out any old regions to prevent buildup.
if ([self.locationManager.monitoredRegions count] > 0)
{
for (id obj in self.locationManager.monitoredRegions)
{
[self.locationManager stopMonitoringForRegion:obj];
}
}
// If the overlay's radius is too large, registration fails automatically,
// so clamp the radius to the max value.
CLLocationDegrees radius = 100; // meters?
if (radius > self.locationManager.maximumRegionMonitoringDistance)
{
radius = self.locationManager.maximumRegionMonitoringDistance;
}
// Create the region to be monitored.
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:location.coordinate radius:radius identifier:kGeofenceIdentifier];
[self.locationManager startMonitoringForRegion:region];
return YES;
}
When the user exits the geofence region, I respond to it like so:
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
if([region.identifier isEqual:kGeofenceIdentifier])
{
if([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
{
// Fire a UILocationNotification
}
else
{
// Fire a UIAlertView
}
}
}
I've determined that the application is able to fetch the users location and that a geofence is correctly registered, however I'm unable to trigger it on an actual device (a 3G-enabled iPhone). I've left the geofenced region and travelled several miles without receiving any kind of notification. I've successfully managed to receive the alert in the Simulator by drastically changing my location.
What am I doing wrong?
Upvotes: 4
Views: 2468
Reputation: 36427
You've already got your answer, but sometimes the problem could be because of not going out for too much and not staying out for too long or sometimes you have done that, but since you're region was like set to something very small like 5 meters (something you should never ever do) and you moved for like 100meters
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 doesn’t deliver region notifications until certain threshold conditions are met. Specifically, the user’s location must cross the region boundary, move away from the 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.
Upvotes: 0
Reputation: 2645
It turns out the solution to this issue was to ensure CLLocationManager was initialised on the main thread. As per Apple's documentation:
Configuration of your location manager object must always occur on a thread with an active run loop, such as your application’s main thread.
Upvotes: 2
Reputation: 14164
It looks like your radius is set to just 100M (good). The distance filter is also set to 100M as well that might just be fighting against you. In the simulator, you get very precise updates because you have to pass them in. In real world testing, you are getting less than precise coordinates from Wifi approximation and cell-triangulation.
My experience is that if I use kCLLocationAccuracyBest
for the distance filter, I get very good results. Since region monitoring is already low powered from the system, you aren't going to take a battery hit just by setting the filter to best.
Upvotes: 1