I am currently trying to get my app to monitor particular regions using CoreLocation
however I am finding that it does not seem to work as expected, it seems to
Based on @Nevan's answer, which indicated some sort of coverage in WWDC 2013 307 (which didn't directly address this), I came up with a reasonable solution to getting < 10m accuracy for the arrival to a location, though I have a feeling that implementing -(void)locationManager:didVisit:
might make this more battery-conservative, but would provide less frequent updates.
First, have some regions with 0..150m radius, and start monitoring. Doesn't really matter, as the system seems to trigger these at around 150~200m:
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(location.lat, location.lng) radius:50 identifier:location.name];
[_locationManager startMonitoringForRegion:region];
Then, implement
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
for (CLCircularRegion *enteredRegion in _locationManager.monitoredRegions.allObjects) {
if ([enteredRegion.identifier isEqualToString:region.identifier]) {
self.locationManager.activityType = CLActivityTypeFitness;
self.locationManager.distanceFilter = 5;
[self.locationManager startUpdatingLocation];
break;
}
}
}
The system will start monitoring and reporting to your delegate a stream of locations, even if your app is suspended (need UIBackgroundModes
to include location
array element).
To check if one of those locations is within the centre of one of your regions, Implement:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *firstLocation = [locations firstObject];
CGFloat const DESIRED_RADIUS = 10.0;
CLCircularRegion *circularRegion = [[CLCircularRegion alloc] initWithCenter:firstLocation.coordinate radius:DESIRED_RADIUS identifier:@"radiusCheck"];
for (CLCircularRegion *enteredRegion in _locationManager.monitoredRegions.allObjects) {
if ([circularRegion containsCoordinate:enteredRegion.center]) {
[_locationManager stopUpdatingLocation];
NSLog(@"You are within %@ of %@, @(DESIRED_RADIUS), enteredRegion.identifier);
break;
} else if ([enteredRegion containsCoordinate:circularRegion.center]) {
NSLog(@"You are within the region, but not yet %@m from %@", @(DESIRED_RADIUS), enteredRegion.identifier);
}
}
}
You'll also want to implement:
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[_locationManager stopUpdatingLocation];
}
I don't think region monitoring will work well for such a small radius.
kCLLocationAccuracyBestForNavigation
is often just 10 meters. startMonitoringForRegion:desiredAccuracy:
which allowed you to specify the distance past the region border to start generating notifications. Presumably this feature has been rolled into startMonitoringForRegion:
but is still there. A 10m region might end up with a 10m buffer.CLCircularRegion
's -containsCoordinate:
to trigger when the device is within 10m manually. This method is officially sanctioned by Apple (see WWDC 2013 Session 307).From the CLCircularRegion
docs:
Remember that the location manager does not generate notifications immediately upon crossing a region boundary. Instead, it applies time and distance criteria to ensure that the crossing was intended and should genuinely trigger a notification. So choose a center point and radius that are appropriate and give you enough time to alert the user.
From the Location & Maps PG:
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.
There's further inside scoop from this post by Kevin McMahon, who asked the Core Location engineers about region monitoring at a lab at WWDC 2012. This info will have changed in the meantime, but the part about region categories is interesting. Here's an edit:
Fine Region (0 - 150m)
- With the floor of 100m this category's range is effectively 100-150m.
- For regions this size performance is heavily dependent on the location-related hardware
- The amount of time that it takes Core Location to detect and call the appropriate delegate method is roughly 2-3 minutes on average after the region boundary has been crossed.
- Some developers have figured out independently that smaller regions would see quicker callbacks and would cluster smaller regions to cover one large area to improve region crossing notifications.
In the past few days iv'e been testing a geofencing feature on my iOS 8.1 device (iPhone 5S) for an app iv'e developed.
The app is registering few regions to the iOS gefence service. The app's logic needs that each geofence radius is between 40 to 80 meters.
I'm seeing so far that in areas with larger number of cell towers and Wifi hot-spots, the geofence detection is good enough on entering regions. That is, in down town areas, business areas etc' the geofence detection is working fine.
Unfortunately, the opposite occurs in areas with few cell towers & wifi networks. My neighborhood, for example, is about 1000 meters width and 500 height (1KM x 0.5KM), and there are no cell towers in it. There are few cell towers thought, on the perimeter that surrounds the neighborhood. Unfortunately In the perimeter of the neighborhood the geofence service detects nothing.
Needless to say that i'm testing with Wifi enabled on the device.
When i test my app on Android: the geofencing service on android 4.3, 4.4 & 5.1 works much better than on iOS. The Android's geofencing service does not detect 100% of region transitions, however it detects 50%-90% of the region transitions.
I conclude the following: If there would have been more cell towers & Wifi hot-spots & if Apple would have improved the geofence service then the detection on iOS devices would have been as good as in Android's.
This seems to be a bug in CLLocationManager
. I've done extensive testing using various region radius configurations and locationManager:didExitRegion
does not fire in an expected way. This seems to be either a rather nasty bug or region monitoring does not happen at all like the documentation suggests. I have the test harness available to anyone who wants it:
http://www.mediafire.com/download/x863zkttltyalk6/LocationTest.zip
Run it in the simulator and start the test by by selecting Debug -> Location -> Freeway Drive in the iOS simulator menu. The number you see is the distance from the center of the monitored region. The background color will be green while the device is within the monitored region and red when outside the region. The text below the distance are event logs.
After running the app, you should see locationManager:didExitRegion
fire at 5319 meters from the monitored region. The route will loop every 37 minutes and you'll see the device exiting the region always at 5319 meters.
I've submitted a radar with Apple (17064346). I'll update this answer once I hear back from them. At least then we'll have some input from the canonical source.
Here's the detailed text sent to Apple:
Using a test app on the iOS simulator as well as on an iPhone 5S the CLLocationManager doesn't seem to fire didExitRegion callbacks in an expected way. Regardless of the radius of the circular region being monitored, the callback won't happen until a threshold of around 5000 meters is hit.
Steps to Reproduce:
1. Run the attached app
2. Start region tracking by selecting Debug -> Location -> Freeway Drive in the iOS simulator
3. Monitor the app. The large # indicates the distance from the center of the watched region.
4. After about 190 seconds and 5300 meters didExitRegion will finally fire.
Ths issue does not seem to be related at all to the size of the region. According to the Apple docs, even small regions are supported:
In iOS 6, regions with a radius between 1 and 400 meters work better on iPhone 4S or later devices. (In iOS 5, regions with a radius between 1 and 150 meters work better on iPhone 4S and later devices.) On these devices, an app can expect to receive the appropriate region entered or region exited notification within 3 to 5 minutes on average, if not sooner.
Although region events don't happen instantaneously, they should happen fairly quickly. From the Apple docs:
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.
This is not at all what I am seeing in the test harness. On the simulator the device will always be 5000+ meters away from the region before a locationManager:didExitRegion
event occurs.
This is more like an important comment. From Region Monitoring and iBeacon
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.
Geofencing works by detecting a user moving from one cell network tower to another cell network tower.
Therefore smallest area you can define is dictated by how close together the cell towers are.
Inside a shopping mall or sports stadium, it might be able to do 10 metres — cell towers are often extremely close together. In a regional area anything smaller than 100km can fail.
If you need smaller areas, you need to use bluetooth instead of cell towers (iBeacons). If there is a bluetooth low energy device in the target area you can set the range to very short (centimetres) or reasonably large (up to 30 metres or so). Note this all depends on the quality of the iBeacon hardware, some are better than others.
Unfortunately bluetooth (version 4.0 or newer) and cell network towers are the only way to monitor locations without significantly draining battery. Keeping the GPS active to check for a 10 metre boundary would drain the battery from full to completely flat in about 2 hours even with the screen switched off.