'iOS requestStateForRegion returning .unknown after didStartMonitoringFor

Confused about why my AppDelegate CLLocationManager sometimes can't successfully determine CLRegionState (CLRegionState.unknown), even though I am making sure to not call requestStateForRegion until after didStartMonitoringForRegion. Seems like some sort of race condition that I don't understand how I'm not handling.

Related, what am I supposed to do when I get CLRegionState.unknown? Keep requesting state until I get something? (that seems bad)

AppDelegate:

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{   
    self.locationManager = [[CLLocationManager alloc] init];
    [self.locationManager setDelegate:self];

    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(nonnull CLRegion *)region {
    NSLog(@"%@", [NSString stringWithFormat:@"didStartMonitoringFor region with ID: %@", region.identifier]);
    // delay per https://stackoverflow.com/a/33288103/3380970
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
        [self.locationManager requestStateForRegion:region];
    });
}

- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(nonnull CLRegion *)region {
    NSLog(@"%@", [NSString stringWithFormat:@"didDetermineState from AppDelegate CLLocationManager: %@ (%ld)", region.identifier, (long)state ]);
    NSString *baseUrl = [self fetchBaseUrl];
    switch (state) {
        case CLRegionStateInside:
            NSLog(@"%@", @"Determined .inside state for geofence");
            [HelperFunctions postGeofenceTransitionWithBaseUrl:baseUrl region:region transitionType:1];
            break;
        default:
            break;
    }
}

Abdriged logs:

Adding geofence with ID: 5:90fd74e0-cbc7-dbe5-20fc-b870203055e1    
Adding geofence with ID: 8:d408f466-ea1b-6cbb-382f-f7567db61157
Adding geofence with ID: 7:ba97dda2-39a7-9b95-8ea9-ea183d69a858

didStartMonitoringFor region with ID: 5:90fd74e0-cbc7-dbe5-20fc-b870203055e1
didStartMonitoringFor region with ID: 8:d408f466-ea1b-6cbb-382f-f7567db61157
didStartMonitoringFor region with ID: 7:ba97dda2-39a7-9b95-8ea9-ea183d69a858

didDetermineState from AppDelegate CLLocationManager: 5:90fd74e0-cbc7-dbe5-20fc-b870203055e1 (0)
didDetermineState from AppDelegate CLLocationManager: 8:d408f466-ea1b-6cbb-382f-f7567db61157 (0)
didDetermineState from AppDelegate CLLocationManager: 7:ba97dda2-39a7-9b95-8ea9-ea183d69a858 (0)

Thanks



Solution 1:[1]

It's because you use the entry parameter region inside a block without making it block variable.

In - (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(nonnull CLRegion *)region { add :

__block CLRegion *blockRegion = region;

and use blockRegion inside you block.

Solution 2:[2]

Be careful whenever you want to use requestStateForRegion. This method may generate incorrect results with respect to the currentLocation of the device. As I did some experiments using different networks if you are on LTE it misses some status for some regions and on WIFI I saw it produces duplicates counter events for example for Location A it produced enter and exit both. I you carefully read the comment for this method under apple documentation it says

Asynchronously retrieve the cached state of the specified region.

This cached state is not accurate.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David 'mArm' Ansermot
Solution 2 Mudassir Zulfiqar