summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gyp/platform-ios.gypi1
-rw-r--r--include/mbgl/ios/MGLMapboxEvents.h4
-rw-r--r--include/mbgl/ios/MGLMetricsLocationManager.h9
-rw-r--r--platform/ios/MGLAccountManager.m10
-rw-r--r--platform/ios/MGLMapView.mm11
-rw-r--r--platform/ios/MGLMapboxEvents.m230
-rw-r--r--platform/ios/MGLMapboxEvents_Private.h7
-rw-r--r--platform/ios/MGLMetricsLocationManager.m13
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 {