Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Switch to KVO for configuration values #95

Merged
merged 3 commits into from
Dec 12, 2019
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 51 additions & 41 deletions platform/ios/src/MGLMapboxEvents.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

static NSString * const MGLAPIClientUserAgentBase = @"mapbox-maps-ios";

static void * MGLMapboxMetricsEnabledKeyContext = &MGLMapboxMetricsEnabledKeyContext;
static void * MGLMapboxMetricsDebugLoggingEnabledKeyContext = &MGLMapboxMetricsDebugLoggingEnabledKeyContext;
static void * MGLTelemetryAccessTokenKeyContext = &MGLTelemetryAccessTokenKeyContext;

@interface MGLMapboxEvents ()

@property (nonatomic) MMEEventsManager *eventsManager;
Expand Down Expand Up @@ -55,8 +59,6 @@ - (instancetype)init {
_eventsManager = MMEEventsManager.sharedManager;
_eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabledKey];

// TODO: What happens if the dev sets MGLMapboxAccountTypeKey prior to using the SDK?
// _eventsManager.accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountTypeKey];
BOOL collectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabledKey];
NSUserDefaults.mme_configuration.mme_isCollectionEnabled = collectionEnabled;

Expand All @@ -74,60 +76,68 @@ - (instancetype)init {
self.baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURLKey]];
}

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil];
// Guard against over calling pause / resume if the values this implementation actually
// cares about have not changed. We guard because the pause and resume method checks CoreLocation's
// authorization status and that can drag on the main thread if done too many times (e.g. if the host
// app heavily uses the user defaults API and this method is called very frequently)
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults addObserver:self
forKeyPath:MGLMapboxMetricsEnabledKey
options:NSKeyValueObservingOptionNew
context:MGLMapboxMetricsEnabledKeyContext];
[defaults addObserver:self
forKeyPath:MGLMapboxMetricsDebugLoggingEnabledKey
options:NSKeyValueObservingOptionNew
context:MGLMapboxMetricsDebugLoggingEnabledKeyContext];
[defaults addObserver:self
forKeyPath:MGLTelemetryAccessTokenKey
options:NSKeyValueObservingOptionNew
context:MGLTelemetryAccessTokenKeyContext];
}
return self;
}

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
@try {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObserver:self forKeyPath:MGLMapboxMetricsEnabledKey];
rclee marked this conversation as resolved.
Show resolved Hide resolved
[defaults removeObserver:self forKeyPath:MGLMapboxMetricsDebugLoggingEnabledKey];
[defaults removeObserver:self forKeyPath:MGLTelemetryAccessTokenKey];
}
@catch (NSException *exception) {} //Purposefully unhandled. If the observer is removed by a superclass this may fail since we are removing it twice.
rclee marked this conversation as resolved.
Show resolved Hide resolved
}

- (void)userDefaultsDidChange:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateNonDisablingConfigurationValues];
[self updateDisablingConfigurationValuesWithNotification:notification];
});
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
//KVO callback can happen on any thread. Even two threads concurrently for the same key.
if (context == MGLMapboxMetricsEnabledKeyContext) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateDisablingConfigurationValues];
});
} else if (context == MGLMapboxMetricsDebugLoggingEnabledKeyContext) {
dispatch_async(dispatch_get_main_queue(), ^{
self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabledKey];
});
} else if (context == MGLTelemetryAccessTokenKeyContext) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateNonDisablingConfigurationValues];
});
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
julianrex marked this conversation as resolved.
Show resolved Hide resolved
}

- (void)updateNonDisablingConfigurationValues {

self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabledKey];

// It is possible for the telemetry access token key to have been set yet `userDefaultsDidChange:`
// is called before `setupWithAccessToken:` is called.
// In that case, setting the access token here will have no effect. In practice, that's fine
// because the access token value will be resolved when `setupWithAccessToken:` is called eventually
if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessTokenKey]) {
NSString *telemetryAccessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessTokenKey];
NSUserDefaults.mme_configuration.mme_accessToken = telemetryAccessToken;
}

}

- (void)updateDisablingConfigurationValuesWithNotification:(NSNotification *)notification {
// Guard against over calling pause / resume if the values this implementation actually
// cares about have not changed. We guard because the pause and resume method checks CoreLocation's
// authorization status and that can drag on the main thread if done too many times (e.g. if the host
// app heavily uses the user defaults API and this method is called very frequently)

if ([[notification object] respondsToSelector:@selector(objectForKey:)]) {
NSUserDefaults *userDefaults = [notification object];

// //TODO: Account type changed at runtime
// NSInteger accountType = [userDefaults integerForKey:MGLMapboxAccountTypeKey];
// if (accountType != self.eventsManager.accountType) {
// self.eventsManager.accountType = accountType;
// }

BOOL oldCollectionEnabled = NSUserDefaults.mme_configuration.mme_isCollectionEnabled;
BOOL collectionEnabled = [userDefaults boolForKey:MGLMapboxMetricsEnabledKey];

if (collectionEnabled != oldCollectionEnabled) {
NSUserDefaults.mme_configuration.mme_isCollectionEnabled = collectionEnabled;
[self.eventsManager pauseOrResumeMetricsCollectionIfRequired];
}
}
- (void)updateDisablingConfigurationValues {
BOOL collectionEnabled = [NSUserDefaults.standardUserDefaults boolForKey:MGLMapboxMetricsEnabledKey];
NSUserDefaults.mme_configuration.mme_isCollectionEnabled = collectionEnabled;

[self.eventsManager pauseOrResumeMetricsCollectionIfRequired];
}

+ (void)setupWithAccessToken:(NSString *)accessToken {
Expand Down Expand Up @@ -173,7 +183,7 @@ + (void)ensureMetricsOptoutExists {
BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue];

if (!metricsEnabledSettingShownInAppFlag &&
[[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountTypeKey] == 0) {
[[NSUserDefaults mme_configuration] integerForKey:MGLMapboxAccountTypeKey] == 0) {
// Opt-out is not configured in UI, so check for Settings.bundle
id defaultEnabledValue;
NSString *appSettingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
Expand Down