summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin R. Miller <incanus@codesorcery.net>2015-03-26 21:27:56 -0700
committerJustin R. Miller <incanus@codesorcery.net>2015-03-26 21:27:56 -0700
commit83f083c7b5854db25168240a4ad7a313f2765f05 (patch)
tree5e1251987b10e565b410024e77da164ebc143852
parent00d2cbe5cbab789388d3b06b899d007b2c44e61a (diff)
downloadqtlocation-mapboxgl-83f083c7b5854db25168240a4ad7a313f2765f05.tar.gz
fixes #1116: metrics stability & performance refactor
-rw-r--r--include/mbgl/ios/MGLMapView.h4
-rw-r--r--include/mbgl/ios/MGLMapboxEvents.h53
-rw-r--r--include/mbgl/ios/MGLMetricsLocationManager.h21
-rw-r--r--platform/ios/MGLMapView.mm107
-rw-r--r--platform/ios/MGLMapboxEvents.m573
-rw-r--r--platform/ios/MGLMetricsLocationManager.m62
6 files changed, 495 insertions, 325 deletions
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
index b41e2f5d72..aa92b0c822 100644
--- a/include/mbgl/ios/MGLMapView.h
+++ b/include/mbgl/ios/MGLMapView.h
@@ -1,8 +1,8 @@
+#import "MGLTypes.h"
+
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
-#import "MGLTypes.h"
-
@class MGLUserLocation;
@protocol MGLMapViewDelegate;
diff --git a/include/mbgl/ios/MGLMapboxEvents.h b/include/mbgl/ios/MGLMapboxEvents.h
index bb28ec26c5..541f21df43 100644
--- a/include/mbgl/ios/MGLMapboxEvents.h
+++ b/include/mbgl/ios/MGLMapboxEvents.h
@@ -1,26 +1,43 @@
-//
-// MapboxEvents.h
-// MapboxEvents
-//
-// Created by Brad Leege on 3/5/15.
-// Copyright (c) 2015 Mapbox. All rights reserved.
-//
-
#import <Foundation/Foundation.h>
+extern NSString *const MGLEventMapLoad;
+extern NSString *const MGLEventMapTap;
+extern NSString *const MGLEventMapSingleTap;
+extern NSString *const MGLEventMapDoubleTap;
+extern NSString *const MGLEventMapTwoFingerSingleTap;
+extern NSString *const MGLEventMapQuickZoom;
+extern NSString *const MGLEventMapPanStart;
+extern NSString *const MGLEventMapPanEnd;
+extern NSString *const MGLEventMapPinchStart;
+extern NSString *const MGLEventMapRotateStart;
+extern NSString *const MGLEventMapLocation;
+
@interface MGLMapboxEvents : NSObject
-@property (atomic) NSInteger flushAt;
-@property (atomic) NSInteger flushAfter;
-@property (atomic) NSString *api;
-@property (atomic) NSString *token;
-@property (atomic) NSString *appName;
-@property (atomic) NSString *appVersion;
+// You must call these methods from the main thread.
+//
++ (void) setToken:(NSString *)token;
++ (void) setAppName:(NSString *)appName;
++ (void) setAppVersion:(NSString *)appVersion;
-+ (id)sharedManager;
+// You can call this method from any thread. Significant work will
+// be dispatched to a low-priority background queue and all
+// resulting calls are guaranteed threadsafe.
+//
+// Events or attributes passed could be accessed on non-main threads,
+// so you must not reference UI elements from within any arguments.
+// Copy any values needed first or create dedicated methods in this
+// class for threadsafe access to UIKit classes.
+//
++ (void) pushEvent:(NSString *)event withAttributes:(NSDictionary *)attributeDictionary;
-- (void) pushEvent:(NSString *)event withAttributes:(NSDictionary *)attributeDictionary;
+// You can call these methods from any thread.
+//
++ (NSString *) checkEmailEnabled;
++ (BOOL) checkPushEnabled;
-- (void) flush;
+// You can call this method from any thread.
+//
++ (void) flush;
-@end \ No newline at end of file
+@end
diff --git a/include/mbgl/ios/MGLMetricsLocationManager.h b/include/mbgl/ios/MGLMetricsLocationManager.h
index ce04ae9ef6..7281d05010 100644
--- a/include/mbgl/ios/MGLMetricsLocationManager.h
+++ b/include/mbgl/ios/MGLMetricsLocationManager.h
@@ -1,22 +1,9 @@
-//
-// MBLocationManager.h
-// Hermes
-//
-// Created by Brad Leege on 3/8/15.
-// Copyright (c) 2015 Mapbox. All rights reserved.
-//
-
#import <Foundation/Foundation.h>
-#import "CoreLocation/CoreLocation.h"
-@interface MGLMetricsLocationManager : NSObject <CLLocationManagerDelegate>
+@interface MGLMetricsLocationManager : NSObject
-+ (id)sharedManager;
-
-- (BOOL) isAuthorizedStatusDetermined;
-- (void) requestAlwaysAuthorization;
-
-- (void) startUpdatingLocation;
-- (void) stopUpdatingLocation;
+// This method can be called from any thread.
+//
++ (instancetype)sharedManager;
@end
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index a4604bc580..92367a3cf2 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -22,7 +22,6 @@
#import "SMCalloutView.h"
#import "MGLMapboxEvents.h"
-#import "MGLMetricsLocationManager.h"
#import <algorithm>
@@ -155,7 +154,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if (accessToken)
{
mbglMap->setAccessToken((std::string)[accessToken UTF8String]);
- [[MGLMapboxEvents sharedManager] setToken:accessToken];
+ [MGLMapboxEvents setToken:accessToken];
}
}
@@ -195,16 +194,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
//
self.accessibilityLabel = @"Map";
- // setup Metrics
- MGLMapboxEvents *events = [MGLMapboxEvents sharedManager];
+ // metrics: initial setup
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
- if (appName != nil) {
- events.appName = appName;
- }
- if (appVersion != nil) {
- events.appVersion = appVersion;
- }
+ if (appName != nil) [MGLMapboxEvents setAppName:appName];
+ if (appVersion != nil) [MGLMapboxEvents setAppVersion:appVersion];
// create GL view
//
@@ -345,9 +339,6 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
- // setup dedicated location manager for metrics
- [MGLMetricsLocationManager sharedManager];
-
// set initial position
//
mbglMap->setLatLngZoom(mbgl::LatLng(0, 0), mbglMap->getMinZoom());
@@ -360,28 +351,18 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// start the main loop
mbglMap->start();
-
- // Fire map.load on a background thread
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
-
- NSMutableDictionary *evt = [[NSMutableDictionary alloc] init];
- [evt setValue:[[NSNumber alloc] initWithDouble:mbglMap->getLatLng().latitude] forKey:@"lat"];
- [evt setValue:[[NSNumber alloc] initWithDouble:mbglMap->getLatLng().longitude] forKey:@"lng"];
- [evt setValue:[[NSNumber alloc] initWithDouble:mbglMap->getZoom()] forKey:@"zoom"];
- [evt setValue:[[NSNumber alloc] initWithBool:[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]] forKey:@"enabled.push"];
-
- NSString *email = @"Unknown";
- Class MFMailComposeViewController = NSClassFromString(@"MFMailComposeViewController");
- if (MFMailComposeViewController) {
- SEL canSendMail = NSSelectorFromString(@"canSendMail");
- BOOL sendMail = ((BOOL (*)(id, SEL))[MFMailComposeViewController methodForSelector:canSendMail])(MFMailComposeViewController, canSendMail);
- email = [NSString stringWithFormat:@"%i", sendMail];
- }
- [evt setValue:email forKey:@"enabled.email"];
+ // metrics: map load event
+ const mbgl::LatLng latLng = mbglMap->getLatLng();
+ const double zoom = mbglMap->getZoom();
+
+ [MGLMapboxEvents pushEvent:MGLEventMapLoad withAttributes:@{
+ @"lat": @(latLng.latitude),
+ @"lng": @(latLng.longitude),
+ @"zoom": @(zoom),
+ @"enabled.push": @([MGLMapboxEvents checkPushEnabled]),
+ @"enabled.email": [MGLMapboxEvents checkEmailEnabled]
+ }];
- [[MGLMapboxEvents sharedManager] pushEvent:@"map.load" withAttributes:evt];
- });
-
return YES;
}
@@ -566,8 +547,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)appDidBackground:(NSNotification *)notification
{
- // Flush Any Events Still In Queue
- [[MGLMapboxEvents sharedManager] flush];
+ [MGLMapboxEvents flush];
mbglMap->stop();
@@ -606,7 +586,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
- [self trackGestureEvent:@"Pan" forRecognizer:pan];
+ [self trackGestureEvent:MGLEventMapPanStart forRecognizer:pan];
if ( ! self.isScrollEnabled) return;
@@ -668,21 +648,22 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[self notifyMapChange:@(mbgl::MapChangeRegionDidChange)];
}
- // Send Map Drag End Event
- CGPoint ptInView = CGPointMake([pan locationInView:pan.view].x, [pan locationInView:pan.view].y);
- CLLocationCoordinate2D coord = [self convertPoint:ptInView toCoordinateFromView:pan.view];
- NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
- [dict setValue:[[NSNumber alloc] initWithDouble:coord.latitude] forKey:@"lat"];
- [dict setValue:[[NSNumber alloc] initWithDouble:coord.longitude] forKey:@"lng"];
- [dict setValue:[[NSNumber alloc] initWithDouble:[self zoomLevel]] forKey:@"zoom"];
+ // metrics: pan end
+ CGPoint pointInView = CGPointMake([pan locationInView:pan.view].x, [pan locationInView:pan.view].y);
+ CLLocationCoordinate2D panCoordinate = [self convertPoint:pointInView toCoordinateFromView:pan.view];
+ double zoom = [self zoomLevel];
- [[MGLMapboxEvents sharedManager] pushEvent:@"map.dragend" withAttributes:dict];
+ [MGLMapboxEvents pushEvent:MGLEventMapPanEnd withAttributes:@{
+ @"lat": @(panCoordinate.latitude),
+ @"lng": @(panCoordinate.longitude),
+ @"zoom": @(zoom)
+ }];
}
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch
{
- [self trackGestureEvent:@"Pinch" forRecognizer:pinch];
+ [self trackGestureEvent:MGLEventMapPinchStart forRecognizer:pinch];
if ( ! self.isZoomEnabled) return;
@@ -720,7 +701,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate
{
- [self trackGestureEvent:@"Rotation" forRecognizer:rotate];
+ [self trackGestureEvent:MGLEventMapRotateStart forRecognizer:rotate];
if ( ! self.isRotateEnabled) return;
@@ -762,7 +743,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
{
- [self trackGestureEvent:@"SingleTap" forRecognizer:singleTap];
+ [self trackGestureEvent:MGLEventMapSingleTap forRecognizer:singleTap];
CGPoint tapPoint = [singleTap locationInView:self];
@@ -885,7 +866,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap
{
- [self trackGestureEvent:@"DoubleTap" forRecognizer:doubleTap];
+ [self trackGestureEvent:MGLEventMapDoubleTap forRecognizer:doubleTap];
if ( ! self.isZoomEnabled) return;
@@ -916,7 +897,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleTwoFingerTapGesture:(UITapGestureRecognizer *)twoFingerTap
{
- [self trackGestureEvent:@"TwoFingerTap" forRecognizer:twoFingerTap];
+ [self trackGestureEvent:MGLEventMapTwoFingerSingleTap forRecognizer:twoFingerTap];
if ( ! self.isZoomEnabled) return;
@@ -949,7 +930,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleQuickZoomGesture:(UILongPressGestureRecognizer *)quickZoom
{
- [self trackGestureEvent:@"QuickZoom" forRecognizer:quickZoom];
+ [self trackGestureEvent:MGLEventMapQuickZoom forRecognizer:quickZoom];
if ( ! self.isZoomEnabled) return;
@@ -997,20 +978,18 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
}
-- (void)trackGestureEvent:(NSString *)gesture forRecognizer:(UIGestureRecognizer *)recognizer
+- (void)trackGestureEvent:(NSString *)gestureID forRecognizer:(UIGestureRecognizer *)recognizer
{
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
- // Send Map Zoom Event
- CGPoint ptInView = CGPointMake([recognizer locationInView:recognizer.view].x, [recognizer locationInView:recognizer.view].y);
- CLLocationCoordinate2D coord = [self convertPoint:ptInView toCoordinateFromView:recognizer.view];
- NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
- [dict setValue:[[NSNumber alloc] initWithDouble:coord.latitude] forKey:@"lat"];
- [dict setValue:[[NSNumber alloc] initWithDouble:coord.longitude] forKey:@"lng"];
- [dict setValue:[[NSNumber alloc] initWithDouble:[self zoomLevel]] forKey:@"zoom"];
- [dict setValue:gesture forKey:@"gesture"];
-
- [[MGLMapboxEvents sharedManager] pushEvent:@"map.click" withAttributes:dict];
- });
+ CGPoint pointInView = CGPointMake([recognizer locationInView:recognizer.view].x, [recognizer locationInView:recognizer.view].y);
+ CLLocationCoordinate2D gestureCoordinate = [self convertPoint:pointInView toCoordinateFromView:recognizer.view];
+ double zoom = [self zoomLevel];
+
+ [MGLMapboxEvents pushEvent:MGLEventMapTap withAttributes:@{
+ @"lat": @(gestureCoordinate.latitude),
+ @"lng": @(gestureCoordinate.longitude),
+ @"zoom": @(zoom),
+ @"gesture": gestureID
+ }];
}
#pragma mark - Properties -
diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m
index 8a037f18f5..7fded59f3e 100644
--- a/platform/ios/MGLMapboxEvents.m
+++ b/platform/ios/MGLMapboxEvents.m
@@ -1,44 +1,86 @@
-//
-// MapboxEvents.m
-// MapboxEvents
-//
-// Dynamic detection of ASIdentifierManager from Mixpanel
-// https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE
-//
-// Created by Brad Leege on 3/5/15.
-// Copyright (c) 2015 Mapbox. All rights reserved.
-//
-
#import "MGLMapboxEvents.h"
+
#import <UIKit/UIKit.h>
+#import <SystemConfiguration/CaptiveNetwork.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
+
+#import "MGLMetricsLocationManager.h"
+
#include <sys/sysctl.h>
-#import <SystemConfiguration/CaptiveNetwork.h>
+
+static NSString *const MGLMapboxEventsUserAgent = @"MapboxEventsiOS/1.0";
+static NSString *const MGLMapboxEventsAPIBase = @"https://api.tiles.mapbox.com";
+
+NSString *const MGLEventMapLoad = @"map.load";
+NSString *const MGLEventMapTap = @"map.click";
+NSString *const MGLEventMapSingleTap = @"SingleTap";
+NSString *const MGLEventMapDoubleTap = @"DoubleTap";
+NSString *const MGLEventMapTwoFingerSingleTap = @"TwoFingerTap";
+NSString *const MGLEventMapQuickZoom = @"QuickZoom";
+NSString *const MGLEventMapPanStart = @"Pan";
+NSString *const MGLEventMapPanEnd = @"map.dragend";
+NSString *const MGLEventMapPinchStart = @"Pinch";
+NSString *const MGLEventMapRotateStart = @"Rotation";
+NSString *const MGLEventMapLocation = @"Location";
+
+//
+// Threadsafety conventions:
+//
+// All variables accessed from more than one thread are
+// designated `atomic` and accessed through dot syntax. The
+// main thread uses underscore syntax during the
+// initialization of the variable.
+//
+// All variables accessed outside of initialization and
+// from within a single thread use underscore syntax.
+//
+// All captures of `self` from within asynchronous
+// dispatches will use a `weakSelf` to avoid cyclical
+// strong references.
+//
@interface MGLMapboxEvents()
-@property (atomic) NSMutableArray *queue;
-@property (atomic) NSString *instance;
-@property (atomic) NSString *anonid;
-@property (atomic) NSTimer *timer;
+// 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) NSString *appName;
+@property (atomic) NSString *appVersion;
+@property (atomic) NSString *instanceID;
+@property (atomic) NSString *anonID;
@property (atomic) NSString *userAgent;
-@property (atomic) dispatch_queue_t serialqPush;
-@property (atomic) dispatch_queue_t serialqFlush;
+@property (atomic) NSString *model;
+@property (atomic) NSString *iOSVersion;
+@property (atomic) NSString *carrier;
+@property (atomic) NSUInteger flushAt;
+@property (atomic) NSTimeInterval flushAfter;
+@property (atomic) NSDateFormatter *rfc3339DateFormatter;
+@property (atomic) CGFloat scale;
+
+// The timer is only ever accessed from the main thread.
+//
+@property (nonatomic) NSTimer *timer;
+
+// This is an array of events to push. All access to it will be
+// from our own serial queue.
+//
+@property (nonatomic) NSMutableArray *eventQueue;
+
+// This is a custom serial queue for accessing the event queue.
+//
+@property (nonatomic) dispatch_queue_t serialQueue;
@end
@implementation MGLMapboxEvents
-static MGLMapboxEvents *sharedManager = nil;
-
-NSDateFormatter *rfc3339DateFormatter = nil;
-NSString *model;
-NSString *iOSVersion;
-NSString *carrier;
-NSNumber *scale;
+// Must be called from the main thread. Only called internally.
+//
+- (instancetype) init {
+ assert([[NSThread currentThread] isMainThread]);
-- (id) init {
self = [super init];
if (self) {
@@ -47,6 +89,8 @@ NSNumber *scale;
if(!settingsBundle) {
NSLog(@"Could not find Settings.bundle");
} else {
+ // Dynamic Settings.bundle loading based on:
+ // http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
@@ -61,16 +105,16 @@ NSNumber *scale;
}
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
- _serialqPush = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.SERIALQPUSH", bundleID, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
- _serialqFlush = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.SERIALQFLUSH", bundleID, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
-
+ _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", bundleID, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
+
// Configure Events Infrastructure
- _queue = [[NSMutableArray alloc] init];
+ _eventQueue = [[NSMutableArray alloc] init];
_flushAt = 20;
- _flushAfter = 10000;
- _api = @"https://api.tiles.mapbox.com";
+ _flushAfter = 60;
_token = nil;
- _instance = [[NSUUID UUID] UUIDString];
+ _instanceID = [[NSUUID UUID] UUIDString];
+ // Dynamic detection of ASIdentifierManager from Mixpanel
+ // https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE
Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
if (ASIdentifierManagerClass) {
SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
@@ -81,84 +125,130 @@ NSNumber *scale;
if (trackingEnabled) {
SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
NSUUID *uuid = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector);
- _anonid = [uuid UUIDString];
+ _anonID = [uuid UUIDString];
} else {
- _anonid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
+ _anonID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
} else {
- _anonid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
+ _anonID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
- model = [self getSysInfoByName:"hw.machine"];
- iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion];
+ _model = [self getSysInfoByName:"hw.machine"];
+ _iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion];
if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
- scale = [[NSNumber alloc] initWithFloat:[UIScreen mainScreen].nativeScale];
+ _scale = [UIScreen mainScreen].nativeScale;
} else {
- scale = [[NSNumber alloc] initWithFloat:[UIScreen mainScreen].scale];
+ _scale = [UIScreen mainScreen].scale;
}
CTCarrier *carrierVendor = [[[CTTelephonyNetworkInfo alloc] init] subscriberCellularProvider];
- carrier = [carrierVendor carrierName];
+ _carrier = [carrierVendor carrierName];
- _userAgent = @"MapboxEventsiOS/1.0";
+ _userAgent = MGLMapboxEventsUserAgent;
// Setup Date Format
- rfc3339DateFormatter = [[NSDateFormatter alloc] init];
+ _rfc3339DateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- [rfc3339DateFormatter setLocale:enUSPOSIXLocale];
- [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
- [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+ [_rfc3339DateFormatter setLocale:enUSPOSIXLocale];
+ [_rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
+ [_rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}
return self;
}
-+ (id)sharedManager {
+// Can be called from any thread. Called implicitly from any
+// public class convenience methods.
+//
++ (instancetype)sharedManager {
static dispatch_once_t onceToken;
+ static MGLMapboxEvents *_sharedManager;
dispatch_once(&onceToken, ^{
- sharedManager = [[self alloc] init];
+ void (^setupBlock)() = ^{
+ _sharedManager = [[self alloc] init];
+ // setup dedicated location manager on first use
+ [MGLMetricsLocationManager sharedManager];
+ };
+ if ( ! [[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ setupBlock();
+ });
+ } else {
+ setupBlock();
+ }
});
- return sharedManager;
+ return _sharedManager;
+}
+
+// Must be called from the main thread.
+//
++ (void) setToken:(NSString *)token {
+ assert([[NSThread currentThread] isMainThread]);
+ [MGLMapboxEvents sharedManager].token = token;
}
+// Must be called from the main thread.
+//
++ (void) setAppName:(NSString *)appName {
+ assert([[NSThread currentThread] isMainThread]);
+ [MGLMapboxEvents sharedManager].appName = appName;
+}
+
+// Must be called from the main thread.
+//
++ (void) setAppVersion:(NSString *)appVersion {
+ assert([[NSThread currentThread] isMainThread]);
+ [MGLMapboxEvents sharedManager].appVersion = appVersion;
+}
+
+// Can be called from any thread. Can be called rapidly from
+// the UI thread, so performance is paramount.
+//
++ (void) pushEvent:(NSString *)event withAttributes:(NSDictionary *)attributeDictionary {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ [[MGLMapboxEvents sharedManager] pushEvent:event withAttributes:attributeDictionary];
+ });
+}
+
+// Can be called from any thread. Called implicitly from public
+// use of +pushEvent:withAttributes:.
+//
- (void) pushEvent:(NSString *)event withAttributes:(NSDictionary *)attributeDictionary {
-
- // Opt Out Checking When Built
- if (![[NSUserDefaults standardUserDefaults] boolForKey:@"mapbox_metrics_enabled_preference"]) {
- [_queue removeAllObjects];
- return;
- }
+ __weak MGLMapboxEvents *weakSelf = self;
- // Add Metrics Disabled App Wide Check
- if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] != nil) {
- [_queue removeAllObjects];
- return;
- }
-
- if (!event) {
- return;
- }
-
- dispatch_async(_serialqPush, ^{
+ dispatch_async(_serialQueue, ^{
+ // Opt Out Checking When Built
+ if (![[NSUserDefaults standardUserDefaults] boolForKey:@"mapbox_metrics_enabled_preference"]) {
+ [_eventQueue removeAllObjects];
+ return;
+ }
+
+ // Add Metrics Disabled App Wide Check
+ if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] != nil) {
+ [_eventQueue removeAllObjects];
+ return;
+ }
+ if (!event) return;
+
NSMutableDictionary *evt = [[NSMutableDictionary alloc] init];
// mapbox-events stock attributes
[evt setObject:event forKey:@"event"];
- [evt setObject:[NSNumber numberWithInt:1] forKey:@"version"];
- [evt setObject:[self formatDate:[NSDate date]] forKey:@"created"];
- [evt setObject:self.instance forKey:@"instance"];
- [evt setObject:self.anonid forKey:@"anonid"];
+ [evt setObject:@(1) forKey:@"version"];
+ [evt setObject:[weakSelf formatDate:[NSDate date]] forKey:@"created"];
+ [evt setObject:weakSelf.instanceID forKey:@"instance"];
+ [evt setObject:weakSelf.anonID forKey:@"anonid"];
// mapbox-events-ios stock attributes
- [evt setValue:[rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
- [evt setValue:model forKey:@"model"];
- [evt setValue:iOSVersion forKey:@"operatingSystem"];
- [evt setValue:[self getDeviceOrientation] forKey:@"orientation"];
- [evt setValue:[[NSNumber alloc] initWithFloat:(100 * [UIDevice currentDevice].batteryLevel)] forKey:@"batteryLevel"];
- [evt setValue:scale forKey:@"resolution"];
- [evt setValue:carrier forKey:@"carrier"];
- [evt setValue:[self getCurrentCellularNetworkConnectionType] forKey:@"cellularNetworkType"];
- [evt setValue:[self getWifiNetworkName] forKey:@"wifi"];
- [evt setValue:[NSNumber numberWithInt:[self getContentSizeScale]] forKey:@"accessibilityFontScale"];
+ [evt setValue:[weakSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
+ [evt setValue:weakSelf.model forKey:@"model"];
+ [evt setValue:weakSelf.iOSVersion forKey:@"operatingSystem"];
+ [evt setValue:[weakSelf getDeviceOrientation] forKey:@"orientation"];
+ [evt setValue:@(100 * [UIDevice currentDevice].batteryLevel) forKey:@"batteryLevel"];
+ [evt setValue:@(weakSelf.scale) forKey:@"resolution"];
+ [evt setValue:weakSelf.carrier forKey:@"carrier"];
+ [evt setValue:[weakSelf getCurrentCellularNetworkConnectionType] forKey:@"cellularNetworkType"];
+ [evt setValue:[weakSelf getWifiNetworkName] forKey:@"wifi"];
+ [evt setValue:@([weakSelf getContentSizeScale]) forKey:@"accessibilityFontScale"];
for (NSString *key in [attributeDictionary allKeys]) {
[evt setObject:[attributeDictionary valueForKey:key] forKey:key];
@@ -168,149 +258,220 @@ NSNumber *scale;
NSDictionary *finalEvent = [NSDictionary dictionaryWithDictionary:evt];
// Put On The Queue
- [self.queue addObject:finalEvent];
+ [_eventQueue addObject:finalEvent];
// Has Flush Limit Been Reached?
- if ((int)_queue.count >= (int)_flushAt) {
- [self flush];
+ if (_eventQueue.count >= weakSelf.flushAt) {
+ [weakSelf flush];
}
// Reset Timer (Initial Starting of Timer after first event is pushed)
- [self startTimer];
-
+ [weakSelf startTimer];
});
}
+// Can be called from any thread.
+//
++ (void) flush {
+ [[MGLMapboxEvents sharedManager] flush];
+}
+
+// Can be called from any thread.
+//
- (void) flush {
- if (_token == nil) {
- return;
- }
-
- dispatch_async(_serialqFlush, ^{
-
- int upper = (int)_flushAt;
- if (_flushAt > [_queue count]) {
- if ([_queue count] == 0) {
+ if (self.token == nil) return;
+
+ __weak MGLMapboxEvents *weakSelf = self;
+
+ dispatch_async(_serialQueue, ^{
+ __block NSArray *events;
+
+ NSUInteger upper = weakSelf.flushAt;
+ if (weakSelf.flushAt > [_eventQueue count]) {
+ if ([_eventQueue count] == 0) {
return;
}
- upper = (int)[_queue count];
+ upper = [_eventQueue count];
}
// Create Array of Events to push to the Server
NSRange theRange = NSMakeRange(0, upper);
- NSArray *events = [_queue subarrayWithRange:theRange];
+ events = [_eventQueue subarrayWithRange:theRange];
// Update Queue to remove events sent to server
- [_queue removeObjectsInRange:theRange];
-
+ [_eventQueue removeObjectsInRange:theRange];
+
// Send Array of Events to Server
- [self postEvents:events];
+ [weakSelf postEvents:events];
});
}
+// Can be called from any thread. Called implicitly from public
+// use of +flush. Posts an async network request to upload metrics.
+//
- (void) postEvents:(NSArray *)events {
- // Setup URL Request
- NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", _api, _token];
- NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
- [request setValue:[self getUserAgent] forHTTPHeaderField:@"User-Agent"];
- [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
- [request setHTTPMethod:@"POST"];
-
- // Convert Array of Dictionaries to JSON
- if ([NSJSONSerialization isValidJSONObject:events]) {
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:events options:NSJSONWritingPrettyPrinted error:nil];
- [request setHTTPBody:jsonData];
+ __weak MGLMapboxEvents *weakSelf = self;
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ // Setup URL Request
+ NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, weakSelf.token];
+ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
+ [request setValue:[weakSelf getUserAgent] forHTTPHeaderField:@"User-Agent"];
+ [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+ [request setHTTPMethod:@"POST"];
- // Send non blocking HTTP Request to server
- [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:nil];
- }
+ // Convert Array of Dictionaries to JSON
+ if ([NSJSONSerialization isValidJSONObject:events]) {
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:events options:NSJSONWritingPrettyPrinted error:nil];
+ [request setHTTPBody:jsonData];
+
+ // Send non blocking HTTP Request to server
+ [NSURLConnection sendAsynchronousRequest:request
+ queue:nil
+ completionHandler:nil];
+ }
+ });
}
+// Can be called from any thread.
+//
- (void) startTimer {
- // Stop Timer if it already exists
- if (_timer) {
- [_timer invalidate];
- _timer = nil;
+ void (^timerBlock)() = ^{
+ // Stop Timer if it already exists
+ if (_timer) {
+ [_timer invalidate];
+ _timer = nil;
+ }
+
+ // Start New Timer
+ NSTimeInterval interval = _flushAfter;
+ _timer = [NSTimer scheduledTimerWithTimeInterval:interval
+ target:self
+ selector:@selector(flush)
+ userInfo:nil repeats:YES];
+ };
+
+ if ( ! [[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ timerBlock();
+ });
+ } else {
+ timerBlock();
}
-
- // Start New Timer
- NSTimeInterval interval = (double)((NSInteger)_flushAfter);
- _timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(flush) userInfo:nil repeats:YES];
}
+// Can be called from any thread.
+//
- (NSString *) getUserAgent {
-
- if (_appName != nil && _appVersion != nil && ([_userAgent rangeOfString:_appName].location == NSNotFound)) {
- _userAgent = [NSString stringWithFormat:@"%@/%@ %@", _appName, _appVersion, _userAgent];
+ if (self.appName != nil && self.appVersion != nil && ([self.userAgent rangeOfString:self.appName].location == NSNotFound)) {
+ self.userAgent = [NSString stringWithFormat:@"%@/%@ %@", self.appName, self.appVersion, self.userAgent];
}
- return _userAgent;
+ return self.userAgent;
}
+// Can be called from any thread.
+//
- (NSString *) formatDate:(NSDate *)date {
- return [rfc3339DateFormatter stringFromDate:date];
+ return [self.rfc3339DateFormatter stringFromDate:date];
}
+// Can be called from any thread.
+//
- (NSString *) getDeviceOrientation {
- switch ([UIDevice currentDevice].orientation) {
- case UIDeviceOrientationUnknown:
- return @"Unknown";
- break;
- case UIDeviceOrientationPortrait:
- return @"Portrait";
- break;
- case UIDeviceOrientationPortraitUpsideDown:
- return @"PortraitUpsideDown";
- break;
- case UIDeviceOrientationLandscapeLeft:
- return @"LandscapeLeft";
- break;
- case UIDeviceOrientationLandscapeRight:
- return @"LandscapeRight";
- break;
- case UIDeviceOrientationFaceUp:
- return @"FaceUp";
- break;
- case UIDeviceOrientationFaceDown:
- return @"FaceDown";
- break;
- default:
- return @"Default - Unknown";
- break;
+ __block NSString *result;
+
+ NSString *(^deviceOrientationBlock)(void) = ^{
+ switch ([UIDevice currentDevice].orientation) {
+ case UIDeviceOrientationUnknown:
+ result = @"Unknown";
+ break;
+ case UIDeviceOrientationPortrait:
+ result = @"Portrait";
+ break;
+ case UIDeviceOrientationPortraitUpsideDown:
+ result = @"PortraitUpsideDown";
+ break;
+ case UIDeviceOrientationLandscapeLeft:
+ result = @"LandscapeLeft";
+ break;
+ case UIDeviceOrientationLandscapeRight:
+ result = @"LandscapeRight";
+ break;
+ case UIDeviceOrientationFaceUp:
+ result = @"FaceUp";
+ break;
+ case UIDeviceOrientationFaceDown:
+ result = @"FaceDown";
+ break;
+ default:
+ result = @"Default - Unknown";
+ break;
+ }
+
+ return result;
+ };
+
+ if ( ! [[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ result = deviceOrientationBlock();
+ });
+ } else {
+ result = deviceOrientationBlock();
}
+
+ return result;
}
-- (int) getContentSizeScale {
- NSString *sc = [UIApplication sharedApplication].preferredContentSizeCategory;
-
- if ([sc isEqualToString:UIContentSizeCategoryExtraSmall]) {
- return -3;
- } else if ([sc isEqualToString:UIContentSizeCategorySmall]) {
- return -2;
- } else if ([sc isEqualToString:UIContentSizeCategoryMedium]) {
- return -1;
- } else if ([sc isEqualToString:UIContentSizeCategoryLarge]) {
- return 0;
- } else if ([sc isEqualToString:UIContentSizeCategoryExtraLarge]) {
- return 1;
- } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraLarge]) {
- return 2;
- } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
- return 3;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityMedium]) {
- return -11;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
- return 10;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
- return 11;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
- return 12;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
- return 13;
+// Can be called from any thread.
+//
+- (NSInteger) getContentSizeScale {
+ __block NSInteger result = -9999;
+
+ NSInteger (^contentSizeScaleBlock)(void) = ^{
+ NSString *sc = [UIApplication sharedApplication].preferredContentSizeCategory;
+
+ if ([sc isEqualToString:UIContentSizeCategoryExtraSmall]) {
+ result = -3;
+ } else if ([sc isEqualToString:UIContentSizeCategorySmall]) {
+ result = -2;
+ } else if ([sc isEqualToString:UIContentSizeCategoryMedium]) {
+ result = -1;
+ } else if ([sc isEqualToString:UIContentSizeCategoryLarge]) {
+ result = 0;
+ } else if ([sc isEqualToString:UIContentSizeCategoryExtraLarge]) {
+ result = 1;
+ } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraLarge]) {
+ result = 2;
+ } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
+ result = 3;
+ } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityMedium]) {
+ result = -11;
+ } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
+ result = 10;
+ } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
+ result = 11;
+ } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
+ result = 12;
+ } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
+ result = 13;
+ }
+
+ return result;
+ };
+
+ if ( ! [[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ result = contentSizeScaleBlock();
+ });
+ } else {
+ result = contentSizeScaleBlock();
}
- return -9999;
-}
+ return result;
+}
+// Can be called from any thread.
+//
- (NSString *)getSysInfoByName:(char *)typeSpecifier
{
size_t size;
@@ -325,6 +486,8 @@ NSNumber *scale;
return results;
}
+// Can be called from any thread.
+//
- (NSString *) getWifiNetworkName {
NSString *ssid = @"";
@@ -343,6 +506,8 @@ NSNumber *scale;
return ssid;
}
+// Can be called from any thread.
+//
- (NSString *) getCurrentCellularNetworkConnectionType {
CTTelephonyNetworkInfo *telephonyInfo = [CTTelephonyNetworkInfo new];
NSString *radioTech = telephonyInfo.currentRadioAccessTechnology;
@@ -376,6 +541,52 @@ NSNumber *scale;
}
}
+// Can be called from any thread.
+//
++ (NSString *) checkEmailEnabled {
+ __block NSString *result;
+
+ NSString *(^mailCheckBlock)(void) = ^{
+ NSString *email = @"Unknown";
+ Class MFMailComposeViewController = NSClassFromString(@"MFMailComposeViewController");
+ if (MFMailComposeViewController) {
+ SEL canSendMail = NSSelectorFromString(@"canSendMail");
+ BOOL sendMail = ((BOOL (*)(id, SEL))[MFMailComposeViewController methodForSelector:canSendMail])
+ (MFMailComposeViewController, canSendMail);
+ email = [NSString stringWithFormat:@"%i", sendMail];
+ }
+ return email;
+ };
+
+ if ( ! [[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ result = mailCheckBlock();
+ });
+ } else {
+ result = mailCheckBlock();
+ }
+
+ return result;
+}
+
+// Can be called from any thread.
+//
++ (BOOL) checkPushEnabled {
+ __block BOOL result;
+
+ BOOL (^pushCheckBlock)(void) = ^{
+ return [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
+ };
+ if ( ! [[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ result = pushCheckBlock();
+ });
+ } else {
+ result = pushCheckBlock();
+ }
-@end \ No newline at end of file
+ return result;
+}
+
+@end
diff --git a/platform/ios/MGLMetricsLocationManager.m b/platform/ios/MGLMetricsLocationManager.m
index 19210fddca..bdf6e14ae9 100644
--- a/platform/ios/MGLMetricsLocationManager.m
+++ b/platform/ios/MGLMetricsLocationManager.m
@@ -1,29 +1,17 @@
-//
-// MBLocationManager.m
-// Hermes
-//
-// Dynamic Settings.bundle loading based on:
-// http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the
-//
-// Created by Brad Leege on 3/8/15.
-// Copyright (c) 2015 Mapbox. All rights reserved.
-//
-
-#import "MGLMetricsLocationManager.h"
-#import "CoreLocation/CoreLocation.h"
#import "MGLMapboxEvents.h"
+#import "MGLMetricsLocationManager.h"
-@interface MGLMetricsLocationManager()
+#import <CoreLocation/CoreLocation.h>
-@property (atomic) CLLocationManager *locationManager;
+@interface MGLMetricsLocationManager() <CLLocationManagerDelegate>
+
+@property (nonatomic) CLLocationManager *locationManager;
@end
@implementation MGLMetricsLocationManager
-static MGLMetricsLocationManager *sharedManager = nil;
-
-- (id) init {
+- (instancetype) init {
if (self = [super init]) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.distanceFilter = 2;
@@ -34,43 +22,31 @@ static MGLMetricsLocationManager *sharedManager = nil;
return self;
}
-+ (id)sharedManager {
++ (instancetype)sharedManager {
static dispatch_once_t onceToken;
+ static MGLMetricsLocationManager *sharedManager;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
-- (BOOL) isAuthorizedStatusDetermined {
- return ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusNotDetermined);
-}
-
-- (void) requestAlwaysAuthorization {
- if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
- [self.locationManager requestAlwaysAuthorization];
- } else {
- // This is iOS 7 or below so Starting Location Updates will trigger authorization request
- [self startUpdatingLocation];
- }
-}
-
-- (void) startUpdatingLocation {
- [self.locationManager startUpdatingLocation];
++ (void) startUpdatingLocation {
+ [[MGLMetricsLocationManager sharedManager].locationManager startUpdatingLocation];
}
-- (void) stopUpdatingLocation {
- [self.locationManager stopUpdatingLocation];
++ (void) stopUpdatingLocation {
+ [[MGLMetricsLocationManager sharedManager].locationManager stopUpdatingLocation];
}
#pragma mark CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
// Iterate through locations to pass all data
for (CLLocation *loc in locations) {
- NSMutableDictionary *evt = [[NSMutableDictionary alloc] init];
- [evt setValue:[[NSNumber alloc] initWithDouble:loc.coordinate.latitude] forKey:@"lat"];
- [evt setValue:[[NSNumber alloc] initWithDouble:loc.coordinate.longitude] forKey:@"lng"];
- [[MGLMapboxEvents sharedManager] pushEvent:@"location" withAttributes:evt];
+ [MGLMapboxEvents pushEvent:MGLEventMapLocation withAttributes:@{
+ @"lat": @(loc.coordinate.latitude),
+ @"lng": @(loc.coordinate.longitude)
+ }];
}
}
@@ -85,18 +61,18 @@ static MGLMetricsLocationManager *sharedManager = nil;
break;
case kCLAuthorizationStatusDenied:
newStatus = @"User Explcitly Denied";
- [[MGLMetricsLocationManager sharedManager] stopUpdatingLocation];
+ [MGLMetricsLocationManager stopUpdatingLocation];
break;
case kCLAuthorizationStatusAuthorized:
newStatus = @"User Has Authorized / Authorized Always";
- [[MGLMetricsLocationManager sharedManager] startUpdatingLocation];
+ [MGLMetricsLocationManager startUpdatingLocation];
break;
// case kCLAuthorizationStatusAuthorizedAlways:
// newStatus = @"Not Determined";
// break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
newStatus = @"User Has Authorized When In Use Only";
- [[MGLMetricsLocationManager sharedManager] startUpdatingLocation];
+ [MGLMetricsLocationManager startUpdatingLocation];
break;
default:
newStatus = @"Unknown";