location manager working on Always but not When In Use

Viewed 378

I have an app that worked great before Apple insisted users be given a choice between "Always" and "When In Use" for Location Manager. The app used iBeacons to send invitations to play games and accept.
When "Always" is selected the beacons work fine but when I switch to "When In Use" they don't work at all. I started out using "Always" but change the following code to give users the choice. In the app's plist I added "Privacy-Location Always and When In Use Usage Descriptions and Privacy-Location When In Use Usage Description" and removed the "Privacy-Location Always Usage Description".

In the app's Delegate I have this

- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status{

    if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedAlways){
        NSLog(@"Always");
        AlertView2 = [[UIAlertView alloc] initWithTitle:@"Dual player on two devices is enabled."
                                                message:@"To save battery power go to Settings/Privacy/Location Services and choose \"Never\" when not using I'M GAME. Two people can still play on one device when in \"Never\" mode. To recieve invitations to play only when the app is open select \"When In Use\"."
                                               delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
        [AlertView2 show];
        [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"accessKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedWhenInUse){
        NSLog(@"WhenInUse");
        AlertView2 = [[UIAlertView alloc] initWithTitle:@"Dual player on two devices is enabled."
                                                message:@"To save battery power go to Settings/Privacy/Location Services and choose \"Never\" when not using I'M GAME. Two people can still play on one device when in \"Never\" mode. To recieve invitations to play while app is in background select \"Always\"."
                                               delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
        [AlertView2 show];
        [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"accessKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted){
        NSLog(@"restricted");
    }

    if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
        NSLog(@"denied");
        AlertView2 = [[UIAlertView alloc] initWithTitle:@"Dual player on a single device Only."
                                                message:@"To play on two devices go to Settings Privacy/Location Services and choose \"Always\" or \"When In Use\" for I'M GAME. In \"Always\" you can recieve invites while app is in background, in \"When In Use\" invites only appear when the app is on screen. To preserve battery choose \"Never\" when not using I'M GAME."
                                               delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
        [AlertView2 show];
        [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"accessKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];

    }
    if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusNotDetermined){
        NSLog(@"undetermined2");
         [locationManager requestAlwaysAuthorization];
       [locationManager requestWhenInUseAuthorization];
    }


}

Does iBeacon need to have Privacy-Location set to "Always" to work?

So I just found out that in "When In Use" you can't monitor for beacon region is entered or exited only find its range. So I guess the question is how would I use range to send a notification to my user.

1 Answers

When you have your app authorized to do beacon ranging only when it is in the foreground, it's easy to simulate entry/exit logic with just a didRangeBeacons callback.

  1. Set up one class variable:

    var beaconLastSeen: Date? = nil
    
  2. Add this code to your didRangeBeacons method:

    if beacons.count > 0 {
      if beaconLastSeen == nil {
         // call didEnterRegion logic here
      }
      beaconLastSeen = Date()
    }
    else {
      if beconLastSeen != nil && Date() - beaconLastSeen > 30 {
         beaconLastSeen = nil
         // call didExitRegion logic here
      }
    }
    

You will get an exit event 30 secs after the last beacon detection. You will get an enter event when one is first seen.

EDIT: Here's the same code in Objective C:

NSDate *beaconLastSeen = nil;

...

if (beacons.count > 0) {
    if (beaconLastSeen == nil) {
        // call didEnterRegion logic here
    }
    beaconLastSeen = [[NSDate alloc] init];
}
else {
    if (beaconLastSeen != nil  && [[[NSDate alloc] init] timeIntervalSinceDate: beaconLastSeen] > 30 ) {
        beaconLastSeen = nil;
        // call didExitRegion logic here
    }
}
Related