xiience
xiience

Reputation: 467

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

Upvotes: 1

Views: 166

Answers (2)

Mudassir Zulfiqar
Mudassir Zulfiqar

Reputation: 375

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.

Upvotes: 0

David Ansermot
David Ansermot

Reputation: 6112

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.

Upvotes: 1

Related Questions