Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Waiting for Location Manager to Init

I'm developing an iOS app based on the map and location tracking. When the user first boots the app it asks for permission to track location etc. just fine. The only problem is while it's doing that, I have code setting up the initial map view and other location-based variables before the user has clicked OK.

I've found I can put these initiation steps after a while loop that waits for the user to change the location manager's permissions as below but this can't possibly be best practice, not to mention it leads to some strange behavior in the transition between the splash screen and the map:

BOOL firstrun = TRUE;
while ([[locationManager class] authorizationStatus] == kCLAuthorizationStatusDenied || [[locationManager class] authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
    NSLog(@"Waiting for location permission");
}
...initiation code...

Is there a "location access granted" listener for the alert box or a similar function in the location manager delegate I don't know about? I see no such method in the docs. Anyone know what the best practice is here? Thank you so much.

EDIT I start my location tracking as follows:

if (nil == locationManager)
    locationManager = [[CLLocationManager alloc] init];

[locationManager startMonitoringSignificantLocationChanges];

self.musicmap.delegate = self;
[self.mymap setShowsUserLocation:true];

Thanks

like image 663
Primus202 Avatar asked Dec 20 '12 22:12

Primus202


2 Answers

I would recommend making your class a CLLocationManagerDelegate and then implementing this method:

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    //Your code goes here
}

More information about CLLocationManagerDelegate can be found here.

Hope that helps!

like image 195
Alex Bardasu Avatar answered Nov 20 '22 20:11

Alex Bardasu


I had a similar problem with my application and the app doing things before the user has time to accept or decline the location permission dialogue. Here is what I ended up doing.

-(BOOL)locationAuthorizationStatus {
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
        // user has not authorized us to use location
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Location Denied", @"Location Denied")
                                                        message:NSLocalizedString(@"This app does not have permission to access your location. Please enable location access in device settings.", @"Message stating this app does not have permission to access your location and to enable location permission in settings")
                                                       delegate:self
                                              cancelButtonTitle:NSLocalizedString(@"Ok", @"Ok")
                                              otherButtonTitles: nil];
        [alert show];
        return NO;
    }

    // Check if region monitoring is available for this device
    if (![CLLocationManager regionMonitoringAvailable]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Geofencing Unavailable", @"Geofencing Unavailable")
                                                        message:NSLocalizedString(@"This device is not able to monitor regions", @"Message stating this device is not able to monitor regions")
                                                       delegate:nil
                                              cancelButtonTitle:nil
                                              otherButtonTitles:NSLocalizedString(@"Ok", @"Ok"), nil];
        [alert show];
        return NO;
    } else {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
            // trigger a location check to prompt user for authorization
            LocationManagerController *locationController = [LocationManagerController sharedManager];
            [locationController.locationManager startUpdatingLocation];
            // the dialogue box is triggered here
            [locationController.locationManager stopUpdatingLocation];
            _waitingOnAuthorization = YES;
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkForAuthorizationStatusChange) name:@"WaitingOnAuthorizationStatus" object:nil];
            return NO;
        }
    }

    return YES;
}

-(void)checkForAuthorizationStatusChange {
    if (_waitingOnAuthorization) {
        // this should only catch location change on first time
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
            // user approved location services

        } else {
            // user declined authorization

        }
        // set flag back to NO
        _waitingOnAuthorization = NO;
    }

    // remove our notification observer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

You will have to add the variables that apply to your use case. But here is the gist of it.

  1. Check for authorization status via BOOL method (locationAuthorizationStatus)
  2. If ok to use location, returns YES do whatever you want
  3. If not, returns NO and alerts user
  4. If first time, will fire up the location manager to trigger location dialogue, then stop it to save battery, sets the flag and sets a notification so you will know when the user has hit yes or no.
  5. The notification fires the method checkAuthorizationChangeStatus and rechecks permissions to see what the user did. From there, you can call any methods you need based on the users choice.

Apple does not have any delegate methods to catch this selection, so the only way around it is kinda of hacky. This method has worked well for me. Slightly hacky, but works. Hope this helps.

like image 4
Bill Burgess Avatar answered Nov 20 '22 22:11

Bill Burgess