diff options
-rw-r--r-- | gyp/platform-ios.gypi | 1 | ||||
-rw-r--r-- | include/mbgl/ios/MGLMapboxEvents.h | 4 | ||||
-rw-r--r-- | include/mbgl/ios/MGLMetricsLocationManager.h | 9 | ||||
-rw-r--r-- | platform/ios/MGLAccountManager.m | 10 | ||||
-rw-r--r-- | platform/ios/MGLMapView.mm | 11 | ||||
-rw-r--r-- | platform/ios/MGLMapboxEvents.m | 230 | ||||
-rw-r--r-- | platform/ios/MGLMapboxEvents_Private.h | 7 | ||||
-rw-r--r-- | platform/ios/MGLMetricsLocationManager.m | 13 |
8 files changed, 130 insertions, 155 deletions
diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 1f96f94702..81c73975a5 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -19,6 +19,7 @@ '../platform/darwin/reachability.m', '../include/mbgl/ios/MapboxGL.h', '../include/mbgl/ios/MGLMapboxEvents.h', + '../platform/ios/MGLMapboxEvents_Private.h', '../platform/ios/MGLMapboxEvents.m', '../include/mbgl/ios/MGLMapView.h', '../include/mbgl/ios/MGLMapView+IBAdditions.h', diff --git a/include/mbgl/ios/MGLMapboxEvents.h b/include/mbgl/ios/MGLMapboxEvents.h index c70c7cb335..abbd3b4b44 100644 --- a/include/mbgl/ios/MGLMapboxEvents.h +++ b/include/mbgl/ios/MGLMapboxEvents.h @@ -32,10 +32,6 @@ extern NSString *const MGLEventGestureRotateStart; // You must call these methods from the main thread. // -+ (void) setToken:(NSString *)token; -+ (void) setAppName:(NSString *)appName; -+ (void) setAppVersion:(NSString *)appVersion; -+ (void) setAppBuildNumber:(NSString *)appBuildNumber; + (void) pauseMetricsCollection; + (void) resumeMetricsCollection; diff --git a/include/mbgl/ios/MGLMetricsLocationManager.h b/include/mbgl/ios/MGLMetricsLocationManager.h index f02c76f53c..4161fc4dd0 100644 --- a/include/mbgl/ios/MGLMetricsLocationManager.h +++ b/include/mbgl/ios/MGLMetricsLocationManager.h @@ -1,13 +1,4 @@ #import <Foundation/Foundation.h> @interface MGLMetricsLocationManager : NSObject - -// These methods can be called from any thread. -// -+ (instancetype)sharedManager; -- (void) startUpdatingLocation; -- (void) stopUpdatingLocation; -- (void) startMonitoringVisits; -- (void) stopMonitoringVisits; - @end diff --git a/platform/ios/MGLAccountManager.m b/platform/ios/MGLAccountManager.m index b1a80fd6a7..ad1e93d29b 100644 --- a/platform/ios/MGLAccountManager.m +++ b/platform/ios/MGLAccountManager.m @@ -1,8 +1,8 @@ #import <Foundation/Foundation.h> #import "MGLAccountManager.h" +#import "MGLMapboxEvents_Private.h" #import "NSProcessInfo+MGLAdditions.h" -#import "MGLMapboxEvents.h" @interface MGLAccountManager() @@ -17,7 +17,7 @@ static MGLAccountManager *_sharedManager; // Can be called from any thread. // -+ (instancetype) sharedInstance { ++ (instancetype) sharedManager { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if ( ! NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) { @@ -38,15 +38,15 @@ static MGLAccountManager *_sharedManager; } + (void) setAccessToken:(NSString *) accessToken { - [[MGLAccountManager sharedInstance] setAccessToken:accessToken]; + [[MGLAccountManager sharedManager] setAccessToken:accessToken]; // Update MGLMapboxEvents // NOTE: This is (likely) the initial setup of MGLMapboxEvents - [MGLMapboxEvents setToken:accessToken]; + [MGLMapboxEvents sharedManager]; } + (NSString *) accessToken { - return [MGLAccountManager sharedInstance].accessToken; + return [MGLAccountManager sharedManager].accessToken; } diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index cd8547e223..80ef4a5258 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -20,6 +20,7 @@ #import "NSString+MGLAdditions.h" #import "NSProcessInfo+MGLAdditions.h" #import "NSException+MGLAdditions.h" +#import "MGLAccountManager.h" #import "MGLAnnotation.h" #import "MGLUserLocationAnnotationView.h" #import "MGLUserLocation_Private.h" @@ -158,7 +159,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) - (void)setAccessToken:(NSString *)accessToken { _mbglMap->setAccessToken(accessToken ? (std::string)[accessToken UTF8String] : ""); - [MGLMapboxEvents setToken:accessToken.mgl_stringOrNilIfEmpty]; + [MGLAccountManager setAccessToken:accessToken.mgl_stringOrNilIfEmpty]; } + (NSSet *)keyPathsForValuesAffectingStyleURL @@ -211,14 +212,6 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration) // self.accessibilityLabel = @"Map"; - // metrics: initial setup - NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; - NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; - if (appName != nil) [MGLMapboxEvents setAppName:appName]; - if (appVersion != nil) [MGLMapboxEvents setAppVersion:appVersion]; - if (appBuildNumber != nil) [MGLMapboxEvents setAppBuildNumber:appBuildNumber]; - // create GL view // _glView = [[GLKView alloc] initWithFrame:self.bounds context:_context]; diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m index 82111fe6ef..d8d57daba9 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -1,10 +1,11 @@ -#import "MGLMapboxEvents.h" +#import "MGLMapboxEvents_Private.h" #import <UIKit/UIKit.h> #import <SystemConfiguration/CaptiveNetwork.h> #import <CoreTelephony/CTTelephonyNetworkInfo.h> #import <CoreTelephony/CTCarrier.h> +#import "MGLAccountManager.h" #import "MGLMetricsLocationManager.h" #import "NSProcessInfo+MGLAdditions.h" #import "NSBundle+MGLAdditions.h" @@ -43,6 +44,76 @@ NSString *const MGLEventGesturePanStart = @"Pan"; NSString *const MGLEventGesturePinchStart = @"Pinch"; NSString *const MGLEventGestureRotateStart = @"Rotation"; +@interface MGLMapboxEventsData : NSObject + +// All of the following properties are written to only from +// the main thread, but can be read on any thread. +// +@property (atomic) NSString *instanceID; +@property (atomic) NSString *advertiserId; +@property (atomic) NSString *vendorId; +@property (atomic) NSString *model; +@property (atomic) NSString *iOSVersion; +@property (atomic) NSString *carrier; +@property (atomic) CGFloat scale; + +@end + +@implementation MGLMapboxEventsData + +- (instancetype)init { + if (self = [super init]) { + _instanceID = [[NSUUID UUID] UUIDString]; + + // Dynamic detection of ASIdentifierManager from Mixpanel + // https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE + _advertiserId = @""; + Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager"); + if (ASIdentifierManagerClass) { + SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager"); + id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector); + // Add check here + SEL isAdvertisingTrackingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled"); + BOOL trackingEnabled = ((BOOL (*)(id, SEL))[sharedManager methodForSelector:isAdvertisingTrackingEnabledSelector])(sharedManager, isAdvertisingTrackingEnabledSelector); + if (trackingEnabled) { + SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier"); + NSUUID *uuid = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector); + _advertiserId = [uuid UUIDString]; + } + } + _vendorId = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; + + _model = [self sysInfoByName:"hw.machine"]; + _iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion]; + if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) { + _scale = [UIScreen mainScreen].nativeScale; + } else { + _scale = [UIScreen mainScreen].scale; + } + CTCarrier *carrierVendor = [[[CTTelephonyNetworkInfo alloc] init] subscriberCellularProvider]; + _carrier = [carrierVendor carrierName]; + } + return self; +} + +// Can be called from any thread. +// +- (NSString *)sysInfoByName:(char *)typeSpecifier +{ + size_t size; + sysctlbyname(typeSpecifier, NULL, &size, NULL, 0); + + char *answer = malloc(size); + sysctlbyname(typeSpecifier, answer, &size, NULL, 0); + + NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding]; + + free(answer); + return results; +} + +@end + // // Threadsafety conventions: // @@ -64,26 +135,18 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; // All of the following properties are written to only from // the main thread, but can be read on any thread. // -@property (atomic) NSString *token; +@property (atomic) MGLMapboxEventsData *data; +@property (atomic) NSString *appBundleId; @property (atomic) NSString *appName; @property (atomic) NSString *appVersion; @property (atomic) NSString *appBuildNumber; -@property (atomic) NSString *instanceID; -@property (atomic) NSString *advertiserId; -@property (atomic) NSString *vendorId; -@property (atomic) NSString *appBundleId; -@property (atomic) NSString *userAgent; -@property (atomic) NSString *model; -@property (atomic) NSString *iOSVersion; -@property (atomic) NSString *carrier; +@property (atomic) MGLMetricsLocationManager *locationManager; @property (atomic) NSUInteger flushAt; @property (atomic) NSDateFormatter *rfc3339DateFormatter; -@property (atomic) CGFloat scale; @property (atomic) NSURLSession *session; @property (atomic) NSData *digicertCert; @property (atomic) NSData *geoTrustCert; - // The paused state tracker is only ever accessed from the main thread. // @property (nonatomic, getter=isPaused) BOOL paused; @@ -133,6 +196,12 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; } } ++ (BOOL)isEnabled { + return ( ! NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent && + [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"] && + [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0); +} + // Must be called from the main thread. Only called internally. // - (instancetype) init { @@ -141,6 +210,10 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; self = [super init]; if (self) { _appBundleId = [[NSBundle mainBundle] bundleIdentifier]; + _appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; + _appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + _appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; + NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString]; _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); @@ -168,38 +241,6 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; _eventQueue = [[NSMutableArray alloc] init]; _flushAt = 20; _flushAfter = 60; - _token = nil; - _instanceID = [[NSUUID UUID] UUIDString]; - - // Dynamic detection of ASIdentifierManager from Mixpanel - // https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE - _advertiserId = @""; - Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager"); - if (ASIdentifierManagerClass) { - SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager"); - id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector); - // Add check here - SEL isAdvertisingTrackingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled"); - BOOL trackingEnabled = ((BOOL (*)(id, SEL))[sharedManager methodForSelector:isAdvertisingTrackingEnabledSelector])(sharedManager, isAdvertisingTrackingEnabledSelector); - if (trackingEnabled) { - SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier"); - NSUUID *uuid = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector); - _advertiserId = [uuid UUIDString]; - } - } - _vendorId = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - - _model = [self sysInfoByName:"hw.machine"]; - _iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion]; - if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) { - _scale = [UIScreen mainScreen].nativeScale; - } else { - _scale = [UIScreen mainScreen].scale; - } - CTCarrier *carrierVendor = [[[CTTelephonyNetworkInfo alloc] init] subscriberCellularProvider]; - _carrier = [carrierVendor carrierName]; - - _userAgent = MGLMapboxEventsUserAgent; // Setup Date Format _rfc3339DateFormatter = [[NSDateFormatter alloc] init]; @@ -224,20 +265,16 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; static dispatch_once_t onceToken; static MGLMapboxEvents *_sharedManager; dispatch_once(&onceToken, ^{ - if ( ! NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent && - [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"] && - [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0) { - void (^setupBlock)() = ^{ - _sharedManager = [[self alloc] init]; - }; - if ( ! [[NSThread currentThread] isMainThread]) { - dispatch_sync(dispatch_get_main_queue(), ^{ - setupBlock(); - }); - } - else { + void (^setupBlock)() = ^{ + _sharedManager = [[self alloc] init]; + }; + if ( ! [[NSThread currentThread] isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ setupBlock(); - } + }); + } + else { + setupBlock(); } }); return _sharedManager; @@ -247,34 +284,6 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; [self pauseMetricsCollection]; } -// Must be called from the main thread. -// -+ (void) setToken:(NSString *)token { - MGLAssertIsMainThread(); - [MGLMapboxEvents sharedManager].token = token; -} - -// Must be called from the main thread. -// -+ (void) setAppName:(NSString *)appName { - MGLAssertIsMainThread(); - [MGLMapboxEvents sharedManager].appName = appName; -} - -// Must be called from the main thread. -// -+ (void) setAppVersion:(NSString *)appVersion { - MGLAssertIsMainThread(); - [MGLMapboxEvents sharedManager].appVersion = appVersion; -} - -// Must be called from the main thread. -// -+ (void) setAppBuildNumber:(NSString *)appBuildNumber { - MGLAssertIsMainThread(); - [MGLMapboxEvents sharedManager].appBuildNumber = appBuildNumber; -} - + (void)pauseMetricsCollection { [[MGLMapboxEvents sharedManager] pauseMetricsCollection]; } @@ -287,11 +296,10 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; return; } self.paused = YES; + _data = nil; [_session invalidateAndCancel]; _session = nil; - MGLMetricsLocationManager *sharedLocationManager = [MGLMetricsLocationManager sharedManager]; - [sharedLocationManager stopUpdatingLocation]; - [sharedLocationManager stopMonitoringVisits]; + _locationManager = nil; } + (void)resumeMetricsCollection { @@ -302,14 +310,13 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; // - (void)resumeMetricsCollection { MGLAssertIsMainThread(); - if (!self.isPaused) { + if (!self.isPaused || [[self class] isEnabled]) { return; } self.paused = NO; + _data = [[MGLMapboxEventsData alloc] init]; _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; - MGLMetricsLocationManager *sharedLocationManager = [MGLMetricsLocationManager sharedManager]; - [sharedLocationManager startUpdatingLocation]; - [sharedLocationManager startMonitoringVisits]; + _locationManager = [[MGLMetricsLocationManager alloc] init]; } // Can be called from any thread. Can be called rapidly from @@ -343,18 +350,18 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; [evt setObject:event forKey:@"event"]; [evt setObject:@(1) forKey:@"version"]; [evt setObject:[strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"]; - [evt setObject:strongSelf.instanceID forKey:@"instance"]; - [evt setObject:strongSelf.advertiserId forKey:@"advertiserId"]; - [evt setObject:strongSelf.vendorId forKey:@"vendorId"]; + [evt setObject:strongSelf.data.instanceID forKey:@"instance"]; + [evt setObject:strongSelf.data.advertiserId forKey:@"advertiserId"]; + [evt setObject:strongSelf.data.vendorId forKey:@"vendorId"]; [evt setObject:strongSelf.appBundleId forKeyedSubscript:@"appBundleId"]; // mapbox-events-ios stock attributes - [evt setValue:strongSelf.model forKey:@"model"]; - [evt setValue:strongSelf.iOSVersion forKey:@"operatingSystem"]; + [evt setValue:strongSelf.data.model forKey:@"model"]; + [evt setValue:strongSelf.data.iOSVersion forKey:@"operatingSystem"]; [evt setValue:[strongSelf deviceOrientation] forKey:@"orientation"]; [evt setValue:@((int)(100 * [UIDevice currentDevice].batteryLevel)) forKey:@"batteryLevel"]; - [evt setValue:@(strongSelf.scale) forKey:@"resolution"]; - [evt setValue:strongSelf.carrier forKey:@"carrier"]; + [evt setValue:@(strongSelf.data.scale) forKey:@"resolution"]; + [evt setValue:strongSelf.data.carrier forKey:@"carrier"]; NSString *cell = [strongSelf currentCellularNetworkConnectionType]; if (cell) { @@ -397,7 +404,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; // Can be called from any thread. // - (void) flush { - if (self.token == nil) return; + if ([MGLAccountManager accessToken] == nil) return; __weak MGLMapboxEvents *weakSelf = self; @@ -435,7 +442,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; if (!strongSelf) return; // Setup URL Request - NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, strongSelf.token]; + NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, [MGLAccountManager accessToken]]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]]; [request setValue:strongSelf.userAgent forHTTPHeaderField:@"User-Agent"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; @@ -483,10 +490,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; // Can be called from any thread. // - (NSString *) userAgent { - if (self.appName != nil && self.appVersion != nil && self.appBuildNumber != nil && ([_userAgent rangeOfString:self.appName].location == NSNotFound)) { - _userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@", self.appName, self.appVersion, self.appBuildNumber, _userAgent]; - } - return _userAgent; + return [NSString stringWithFormat:@"%@/%@/%@ %@", self.appName, self.appVersion, self.appBuildNumber, MGLMapboxEventsUserAgent]; } // Can be called from any thread. @@ -592,22 +596,6 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; // Can be called from any thread. // -- (NSString *)sysInfoByName:(char *)typeSpecifier -{ - size_t size; - sysctlbyname(typeSpecifier, NULL, &size, NULL, 0); - - char *answer = malloc(size); - sysctlbyname(typeSpecifier, answer, &size, NULL, 0); - - NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding]; - - free(answer); - return results; -} - -// Can be called from any thread. -// - (NSString *) wifiNetworkName { NSString *ssid = nil; diff --git a/platform/ios/MGLMapboxEvents_Private.h b/platform/ios/MGLMapboxEvents_Private.h new file mode 100644 index 0000000000..6a48b31f04 --- /dev/null +++ b/platform/ios/MGLMapboxEvents_Private.h @@ -0,0 +1,7 @@ +#import "MGLMapboxEvents.h" + +@interface MGLMapboxEvents (Private) + ++ (instancetype)sharedManager; + +@end diff --git a/platform/ios/MGLMetricsLocationManager.m b/platform/ios/MGLMetricsLocationManager.m index 9c186e4bf8..ce49907403 100644 --- a/platform/ios/MGLMetricsLocationManager.m +++ b/platform/ios/MGLMetricsLocationManager.m @@ -27,17 +27,16 @@ // Clear Any System TimeZone Cache [NSTimeZone resetSystemTimeZone]; [_rfc3339DateFormatter setTimeZone:[NSTimeZone systemTimeZone]]; + + [self startUpdatingLocation]; + [self startMonitoringVisits]; } return self; } -+ (instancetype)sharedManager { - static dispatch_once_t onceToken; - static MGLMetricsLocationManager *sharedManager; - dispatch_once(&onceToken, ^{ - sharedManager = [[self alloc] init]; - }); - return sharedManager; +- (void)dealloc { + [self stopUpdatingLocation]; + [self stopMonitoringVisits]; } - (void)startUpdatingLocation { |