I'm building an iOS 8 app that uses GeoFencing. I'm having some problems that I will explain later. But first, this is how I create a region and monitor it:
Variables in header file:
#property (nonatomic, strong) CLLocationManager *locationManager;
viewDidLoad:
// Initialize locationManager
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager requestAlwaysAuthorization];
// Set the delegate
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLLocationAccuracyBest;
Creating a region:
- (CLRegion*)mapDictionaryToRegion:(NSDictionary*)dictionary
{
NSString *title = [dictionary valueForKey:#"title"];
CLLocationDegrees latitude = [[dictionary valueForKey:#"latitude"] doubleValue];
CLLocationDegrees longitude =[[dictionary valueForKey:#"longitude"] doubleValue];
CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake(latitude, longitude);
CLLocationDistance regionRadius = [[dictionary valueForKey:#"radius"] doubleValue];
return [[CLRegion alloc] initCircularRegionWithCenter:centerCoordinate
radius:regionRadius
identifier:title];
}
Start monitoring it:
-(void)startMonitoringForDictionary:(NSDictionary *)dictionary
{
// Start monitoring for the supplied data
CLRegion *region = [self mapDictionaryToRegion:dictionary];
[self.locationManager startMonitoringForRegion:region];
}
Listening for region crossing:
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Error: %#", error);
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"didEnterRegion");
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(#"exited region");
}
I've remembered these keys in my target info:
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
Problem:
Sometimes when I enter a region, it works flawless. Both didEnterRegion and didExitRegion is called like expected. But it appears as if I add the region while inside it, and then exit - it won't call didExitRegion. Only if it calls didEnterRegion first.
What I'm asking:
Could someone please explain the "rules" for region monitoring and how the device manages it, reports it, wakes app, etc? Also, I read in the documentation that I shouldn't expect updates more frequently than every 5 minutes. (This thing I'm making to practice calculates how long you've spent inside a region) - what if the user enters then exists quickly - then didExitRegion will never be called until I enter again?
Thanks!
Erik
You must call requestState immediately when calling startMonitoringForRegion
Related
Having a bit of an issue. I have a GMSMapView in my application and it points to the users location upon loading. When the app loads I have the locationManager set to display the latitude in the log once it finds the users position. Right now it displays the latitude over 3 times (I plan to make this post to my server). When I launch my application I will have quite a few users and the extra 3 server requests per user would be a lot for a server to handle. I am wondering is there anything I have done wrong in the code or is there a way to get around this? Will attach the code below.
Thanks!
- (void)viewDidLoad
{
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager setDelegate:(id)self];
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager startMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[self.locationManager stopUpdatingLocation];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:newLocation.coordinate.latitude
longitude:newLocation.coordinate.longitude
zoom:17.0];
[self.mapView animateToCameraPosition:camera];
latitude = newLocation.coordinate.latitude;
longitude = newLocation.coordinate.longitude;
NSLog(#"latitude = %g", latitude);
}
didUpdateToLocation was deprecated way back in iOS6. Use the replacement didUpdateLocations, and then just pick off the last element of the array e.g.:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
location = [locations lastObject];
// ...
latitude = location.coordinate.latitude;
longitude = location.coordinate.longitude;
I recently asked this question, not knowing of the changes made to CL in iOS 9. I am trying to store the user's current location and I have changed my code to reflect the new delegate methods in iOS 9, but didUpdateLocations is still never reached.
Here is the link to my original question: Latitude & Longitude Not Retrieved
And my updated code:
viewWillAppear
- (void)viewWillAppear:(BOOL)animated{
manager = [[CLLocationManager alloc] init];
manager.delegate = self;
if ([manager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[manager requestWhenInUseAuthorization];
}
[manager startUpdatingLocation];
}
didUpdateLocations
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray<CLLocation *> *)locations{
NSLog(#"Location: %#", locations);
CLLocation *currentLocation = locations.lastObject;
if (currentLocation != nil) {
float latitude = currentLocation.coordinate.latitude;
float longitude = currentLocation.coordinate.longitude;
NSLog(#"dLongitude : %f", longitude);
NSLog(#"dLatitude : %f", latitude);
}
else{ NSLog(#"ERROR");
}
}
check for
authorizationStatus and startUpdatingLocation
and
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
you can also go to settings ->privacy -> location services (turn on) -> select ur app and select while using.
I have been trying to fake my location in the iOS simulator but the methods I have been reading about are not working. I have used the debugger to set my fake location, and have made a custom location in Debug->Location->Custom Location, yet I still log 0.000 0.000 for lat and long.
I'm using this code to find current location.
- (IBAction)currentLocationButtonPressed:(UIButton *)sender
{
//Search for pubs and bars in current location.
//Push to tableViewController
NSLog(#"current location Pressed.");
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyBest; // Best accuracy
[locationManager startUpdatingLocation];
NSLog(#"%#",[self deviceLocation]);
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
NSLog(#"Latitude: %f Longitude: %f", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude);
}
}
- (NSString *)deviceLocation {
return [NSString stringWithFormat:#"latitude: %f longitude: %f", locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude];
}
The delegate is set and I am requesting authorization.
- (void)viewDidLoad {
[super viewDidLoad];
searchLocationTextField.delegate = self;
locationManager.delegate = self;
locationManager = [[CLLocationManager alloc]init];
[locationManager requestWhenInUseAuthorization]; // For foreground access
[locationManager requestAlwaysAuthorization]; // For background access
}
Set locationManager delegate to after allocating it.
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager:didUpdateToLocation:fromLocation: delegate method is deprecated, use locationManager:didUpdateLocations: instead.
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *currentLocation = [locations lastObject];
NSLog(#"Latitude: %f Longitude: %f", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude);
}
Hiii,
I implemented Geo fences by using location manager.
Here i am creating regions
enter code here
In view did load i create object as
//for Geo fence tracking
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLLocationAccuracyBest;
[locationManager startMonitoringSignificantLocationChanges];
[locationManager startUpdatingLocation];
-(void)TraceGeofenceLocation{
for (int k=0; k< [self.chGeofenceType count]; k++) {
NSString *chTracLati = [NSString stringWithFormat:#"%f",[[self.chGeofenceLatitudes objectAtIndex:k] doubleValue]];
NSString *chTracLong = [NSString stringWithFormat:#"%f",[[self.chGeofeceLongitudes objectAtIndex:k] doubleValue]];
NSString *chTracRadius = [NSString stringWithFormat:#"%f",[[self.chGeofenceRadius objectAtIndex:k] doubleValue]];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(chTracLati.doubleValue, chTracLong.doubleValue);
if ([[self.chGeofenceType objectAtIndex:k] intValue] == 1) {
region = [[CLRegion alloc]initCircularRegionWithCenter:coord radius:chTracRadius.doubleValue identifier:#"Restricted"];
}
else if ([[self.chGeofenceType objectAtIndex:k] intValue] == 2){
region = [[CLRegion alloc]initCircularRegionWithCenter:coord radius:chTracRadius.doubleValue identifier:#"SafeZone"];
}
else{
region = [[CLRegion alloc]initCircularRegionWithCenter:coord radius:chTracRadius.doubleValue identifier:#"Curfew"];
}
[region setNotifyOnEntry:YES];
[region setNotifyOnExit:YES];
[locationManager startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyBest];
}
}
I tested in my delegate methods if any region it will enter or not.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
locationnew = locations.lastObject;
self.Latit=[NSString stringWithFormat:#"%f", locationnew.coordinate.latitude];
self.Longi=[NSString stringWithFormat:#"%f",locationnew.coordinate.longitude];
Speed = [[NSString stringWithFormat:#"%f",[locationnew speed]] floatValue];
NSLog(#"Speed :%f Latitude :%# Longitude :%#",Speed,self.Latit,self.Longi);
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
if ([region.identifier isEqualToString:#"Restricted"]) {
[self sendNotificationtoServerwithtype:#"1"];
}
else if ([region.identifier isEqualToString:#"Curfew"]){
[self sendNotificationtoServerwithtype:#"3"];
}
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
if ([region.identifier isEqualToString:#"SafeZone"]){
[self sendNotificationtoServerwithtype:#"2"];
}
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (state == CLRegionStateInside){
NSLog(#"is in target region");
}else{
NSLog(#"is out of target region");
}
}
My delegate method update locations are calling and i get in console,
but enter into region,exit from regions and did DetermineState are not calling...
Can anybody help me please.
Thanks In Advance
startUpdatingLocation and startMonitoringSignificantLocationChanges are mutually exclusive. If you want to do startMonitoringSignificantLocationChanges to look for geofences, make sure you call stopUpdatingLocation first.
Also be aware that geofence detection is not incredibly precise. On average, you can a resolution of hundreds of meters. I've found that didEnterRegion often take several minutes to be called, if at all.
NOTE: you are currently calling [locationManager startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyBest]; The problem with this is kCLLocationAccuracyBest is a really small area. (kCLLocationAccuracyBest codes for the value 1.0). That means you are asking the system to let you know when you enter an area with a diameter 1.0 meter. Since geofence detection has accuracy of hundreds of meters, this may never get called. Instead you should set the accuracy to something much lower: [locationManager startMonitoringForRegion:region desiredAccuracy:200.0];
Good luck.
I'm having an issue where my app will not fire the didEnterRegion event if I start the app within the region. If I start the app outside the region and then enter the region, it fires. If I start the app inside the region, then leave the region, then re-enter the region, it fires.
Any suggestions on how to get it to fire as soon as the app is opened if it's in the region would be much appreciated!
I suggest you to use this code
[locationManager requestStateForRegion:region];
And use the delegate method didDetermineState: to check if the state is CLRegionStateInside or CLRegionStateOutside.
I don't think you can do that.
But, you can get the current location and check if it's inside the region you're specifying yourself. CLCircularRegion has a containsCoordinate: method for this.
The first conclusion is that didEnterRegion is implemented consistently with its name. :)
Implement something like this in your CLLocationManagerDelegate:
- (void) locationManager: (CLLocationManager *) manager
didStartMonitoringForRegion: (CLRegion *) region
{
if ([self insideRegion: region location: manager.location])
[self locationManager: manager
didEnterRegion: region];
}
From apple's documentation:
Monitoring of a geographical region begins immediately after
registration for authorized apps. However, don’t expect to receive an
event right away, because only boundary crossings generate an event.
In particular, if the user’s location is already inside the region
at registration time, the location manager doesn’t automatically
generate an event. Instead, your app must wait for the user to cross
the region boundary before an event is generated and sent to the
delegate. To check whether the user is already inside the boundary
of a region, use the requestStateForRegion: method of the
CLLocationManager class.
So I ended up doing this:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) NSDictionary *regionDictionary;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// setup regions in case you have multiple regions
self.regionDictionary = #{#"com.test" : #"2FAE2A83-1634-443B-8A0C-56704F81A181"};
// setup location manager
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation];
//start monitoring for all regions
for (NSString *key in self.regionDictionary.allKeys) {
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:self.regionDictionary[key]] identifier:key];
[self.locationManager startMonitoringForRegion:beaconRegion];
}
}
- (void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion *)region {
if (region.identifier.length != 0) {
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:self.regionDictionary[region.identifier]] identifier:region.identifier];
[self.locationManager startRangingBeaconsInRegion:beaconRegion];
}
}
- (void)locationManager:(CLLocationManager*)manager didExitRegion:(CLRegion *)region {
if (region.identifier.length != 0) {
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:self.regionDictionary[region.identifier]] identifier:region.identifier];
[self.locationManager stopRangingBeaconsInRegion:beaconRegion];
}
}
- (void)locationManager:(CLLocationManager*)manager didRangeBeacons:(NSArray*)beacons inRegion:(CLBeaconRegion*)region {
// Beacon found!
CLBeacon *foundBeacon = [beacons firstObject];
NSLog(#"UUID:%#; major:%#; minor:%#;", foundBeacon.proximityUUID.UUIDString, foundBeacon.major, foundBeacon.minor);
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]] && state == CLRegionStateInside) {
[self locationManager:manager didEnterRegion:region];
}
}
- (void)locationManager:(CLLocationManager *) manager didStartMonitoringForRegion:(CLRegion *) region {
[manager requestStateForRegion:region];
}