8

An app I'm working on currently sets up region monitoring with the user's current location when the app is backgrounded. When the app becomes active again I am trying to stop monitoring for the region, but it seems to work intermittently with the majority of the time resulting in it failing to act as expected. When the app is backgrounded, I start monitoring for the region and it works fine when I log the details:

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
    DDLogInfo(@"CREATED REGION: %@", region.identifier);
}

Which results in the following log:

  • "CREATED REGION: regionFor: [real lat here, real lon here] with radius: 100"

When the app wakes up, I call the following function:

- (void)stopMonitoringAllRegions {
    DDLogInfo(@"About to stop monitoring for %d regions", [locationManager monitoredRegions].count);

    // stop monitoring for any and all current regions
    for (CLRegion *region in [[locationManager monitoredRegions] allObjects]) {
        [locationManager stopMonitoringForRegion:region];
    }

    DDLogInfo(@"After stopping, we're currently monitoring for %d regions", [locationManager monitoredRegions].count);
} 

Which results in the following log about 75% of the time:

  • "About to stop monitoring for 1 regions"
  • "After stopping, we're currently monitoring for 1 regions"

and infrequently I get what seems like a success:

  • "About to stop monitoring for 1 regions"
  • "After stopping, we're currently monitoring for 0 regions"

I've tried a couple of things with no success. The regions I'm creating are CLCircularRegions, which inherit from CLRegion so that should work regardless, but in the for-loop I've changed CLRegion to CLCircularRegion with no effect. I was originally using [locationManager monitoredRegions] by itself, which returns an NSSet, so I thought using the allObjects function to get the array would fix the issue, but it hasn't.

I also thought it might be an issue with mutating the array while enumerating, but the only other post I saw on SO said that the above worked for them...

Am I missing something?

Mike
  • 9,765
  • 5
  • 34
  • 59
  • When I was working on my previous app, I come across the same kind of issue. Apparently locationManager won't clear the monitoredRegions right away. [self performSelector:@selector(someMethod) withObject:nil afterDelay:0.1]; // someMethod -> dummy method to print count of monitored regions. – csk Feb 06 '14 at 18:31

1 Answers1

7

If you read up on monitoredRegions, it represents all monitored regions of all CLLocationManager instances, and so is probably controlled by a private dispatch queue - which would explain the delays.

My suggestion would be to keep your own mutable array (or set) around, using it to keep track of what regions are monitored and which are not, and not rely on the location manager for that collection.

Now that its clear you cannot rely on immediate changes to it, I'd design around it rather than try to find some heuristic that seems (today) to work but bites you later.

David H
  • 40,852
  • 12
  • 92
  • 138
  • Interesting. My app only needs to monitor for one region at a time, so all I'm interested in doing is stopping monitoring for the old region and then starting monitoring for a new one. I think the issue I was seeing is only really an issue related to wanting to log all the details - I think my implementation will likely work as intended - testing now. – Mike Feb 06 '14 at 20:58
  • @Mike agreed, it more a cosmetic issue. But if you could by chance inadvertently change something, you might get into trouble. I am playing around with beacons, and had a bug where I asked the location manager to startMonitoring twice - that broke everything! Its still showed the region as being managed, but nothing happened from then on. – David H Feb 06 '14 at 22:01
  • If inside my `didExitRegion` I don't do `manager.stopMonitoringForRegion(region)` would I just an endless number of callbacks each time my location is checked? – mfaani Jul 22 '17 at 10:40
  • @Honey dispatch a block to the main thread to stop monitoring. May need to set a flag that this has been done, then ignore any other `didExitRegions`. – David H Jul 23 '17 at 21:38