Search code examples
iosobjective-cnsuserdefaultscllocationmanagernsnotificationcenter

NSUserDefaultsDidChangeNotification Doesn't Fire After Sending stopUpdatingLocation to CLLocationManager


Currently in this iOS application I have added some controller I've written as an observer to the [NSNotificationCenter defaultCenter] to observe NSUserDefaultsDidChangeNotification.

- (void)startObservingDefaults
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(userDefaultsDidChange)
                                                 name:NSUserDefaultsDidChangeNotification
                                               object:nil];
}

The user default that I am watching should enable or disable a CLLocationManager using a PSToggleSwitchSpecifier from the settings menu. After starting the application and sending startUpdatingLocation to a CLLocationManager ivar I can go to the settings menu and disable the location manager. My selector userDefaultsDidChange will then check if that default is enabled or disabled. If it is enabled, I send startUpdatingLocation to the location manager instance, if it is disabled I send stopUpdatingLocation to it. Each path logs its action like so

- (void)userDefaultsDidChange
{
    if ([self duringOperationHours] && [self isEnabled]) {
        NSLog(@"\n\n\nWithin Operating Hours.\n\n\n");
        [_locationManager startUpdatingLocation];
        NSLog(@"Enabled Location Manager.");
    } else {
        NSLog(@"\n\n\nDisabled or Outside of Operating Hours.\n\n\n");
        [_locationManager stopUpdatingLocation];
        NSLog(@"Disabled Location Manager.");
    }
}

I have other settings that control time of day (start time and end time ex. 6AM to 6PM). If I change one of those settings and the current time is still within that time range the log will show that it is still "Within Operating Hours." and that the location manager is enabled.

If I change it so that the location manager gets disabled, it does log both NSLog statements in my method above and it DOES shut down the location manager.

The problem comes when I try to start it back up. So say I am in the settings and it is enabled. I disable it, the log shows it is disabled and the little icon for GPS disappears, if I then re-enable it the notification doesn't fire at all. In fact, no other settings even in other child panes don't fire until I go back to the application. (I've tried a dispatch_async inside the userDefaultsDidChange method to both main_queue and global_queue and saw no change).

MY QUESTION IS am I doing something wrong that might cause this to lock up with the location manager? Or is this some internal queue issue to CLLocationManager? Or something else? Perhaps CLLocationManager will only start if the application is in the foreground? Even if that were the case I should still be seeing the log statements that it at least attempted to start the location manager right?


Solution

  • Nevermind I figured it out.

    The observation of the NSNotificationCenter on the NSUserDefaultsDidChangeNotification DOES NOT wake up the application. What was happening was the CLLocationManagerDelegate protocol applied to my controller would "wake up" the application to do what it needed to the with CLLocations that were received. Upon this waking up, the observation of change to some user defaults in the NSNotificationCenter would fire off my selector userDefaultsDidChange.

    As long as the CLLocationManager was updating and there to alert the application it would allow the controller to call the designated selector. When the CLLocationManager was no longer updating the location the application would not be woken up and therefore not know any user defaults had changed until the app returned to the foreground.

    I will update this answer if I come up with a better solution for this odd observation scenario.