summaryrefslogtreecommitdiff
path: root/platform/ios/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/src')
-rw-r--r--platform/ios/src/MGLAPIClient.h15
-rw-r--r--platform/ios/src/MGLAPIClient.m202
-rw-r--r--platform/ios/src/MGLLocationManager.h25
-rw-r--r--platform/ios/src/MGLLocationManager.m175
-rw-r--r--platform/ios/src/MGLMapView.mm41
-rw-r--r--platform/ios/src/MGLMapboxEvents.h39
-rw-r--r--platform/ios/src/MGLMapboxEvents.m831
-rw-r--r--platform/ios/src/Mapbox-Prefix.pch1
8 files changed, 130 insertions, 1199 deletions
diff --git a/platform/ios/src/MGLAPIClient.h b/platform/ios/src/MGLAPIClient.h
deleted file mode 100644
index 4e5ea3b5e0..0000000000
--- a/platform/ios/src/MGLAPIClient.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLMapboxEvents.h"
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface MGLAPIClient : NSObject <NSURLSessionDelegate>
-
-- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
-- (void)postEvent:(MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
deleted file mode 100644
index 8a987d76d8..0000000000
--- a/platform/ios/src/MGLAPIClient.m
+++ /dev/null
@@ -1,202 +0,0 @@
-#import "MGLAPIClient.h"
-#import "NSBundle+MGLAdditions.h"
-#import "NSData+MGLAdditions.h"
-#import "MGLAccountManager.h"
-
-static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS";
-static NSString * const MGLAPIClientBaseURL = @"https://events.mapbox.com";
-static NSString * const MGLAPIClientEventsPath = @"events/v2";
-
-static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent";
-static NSString * const MGLAPIClientHeaderFieldContentTypeKey = @"Content-Type";
-static NSString * const MGLAPIClientHeaderFieldContentTypeValue = @"application/json";
-static NSString * const MGLAPIClientHeaderFieldContentEncodingKey = @"Content-Encoding";
-static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
-
-@interface MGLAPIClient ()
-
-@property (nonatomic, copy) NSURLSession *session;
-@property (nonatomic, copy) NSURL *baseURL;
-@property (nonatomic, copy) NSData *digicertCert_2016;
-@property (nonatomic, copy) NSData *geoTrustCert_2016;
-@property (nonatomic, copy) NSData *digicertCert_2017;
-@property (nonatomic, copy) NSData *geoTrustCert_2017;
-@property (nonatomic, copy) NSData *testServerCert;
-@property (nonatomic, copy) NSString *userAgent;
-@property (nonatomic) BOOL usesTestServer;
-
-@end
-
-@implementation MGLAPIClient
-
-- (instancetype)init {
- self = [super init];
- if (self) {
- _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
- delegate:self delegateQueue:nil];
- [self loadCertificates];
- [self setupBaseURL];
- [self setupUserAgent];
- }
- return self;
-}
-
-#pragma mark Public API
-
-- (void)postEvents:(nonnull NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
- __block NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:[self requestForEvents:events]
- completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
- NSError *statusError = nil;
- if (httpResponse.statusCode >= 400) {
- NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_DESC", nil, nil, @"The session data task failed. Original request was: %@", nil), dataTask.originalRequest];
- NSString *reason = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_REASON", nil, nil, @"The status code was %ld", nil), (long)httpResponse.statusCode];
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description,
- NSLocalizedFailureReasonErrorKey: reason};
- statusError = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
- }
- if (completionHandler) {
- error = error ?: statusError;
- completionHandler(error);
- }
- dataTask = nil;
- }];
- [dataTask resume];
-}
-
-- (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
- [self postEvents:@[event] completionHandler:completionHandler];
-}
-
-#pragma mark Utilities
-
-- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- NSString *path = [NSString stringWithFormat:@"%@?access_token=%@", MGLAPIClientEventsPath, [MGLAccountManager accessToken]];
- NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
- NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
- [request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey];
- [request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey];
- [request setHTTPMethod:MGLAPIClientHTTPMethodPost];
-
- NSData *jsonData = [self serializedDataForEvents:events];
-
- // Compressing less than 3 events can have a negative impact on the size.
- if (events.count > 2) {
- NSData *compressedData = [jsonData mgl_compressedData];
- [request setValue:@"deflate" forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
- [request setHTTPBody:compressedData];
- }
-
- // Set JSON data if events.count were less than 3 or something went wrong with compressing HTTP body data.
- if (!request.HTTPBody) {
- [request setValue:nil forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
- [request setHTTPBody:jsonData];
- }
-
- return [request copy];
-}
-
-- (void)setupBaseURL {
- NSString *testServerURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"MGLTelemetryTestServerURL"];
- NSURL *testServerURL = [NSURL URLWithString:testServerURLString];
- if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) {
- self.baseURL = testServerURL;
- self.usesTestServer = YES;
- } else {
- self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL];
- }
-}
-
-- (void)loadCertificates {
- NSData *certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2016"];
- self.geoTrustCert_2016 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2016"];
- self.digicertCert_2016 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2017"];
- self.geoTrustCert_2017 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2017"];
- self.digicertCert_2017 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_staging"];
- self.testServerCert = certificate;
-}
-
-- (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource {
- NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle];
- NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der"];
- if (cerPath != nil) {
- *certificate = [NSData dataWithContentsOfFile:cerPath];
- }
-}
-
-- (void)setupUserAgent {
- NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
- NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
- NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
- NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
- NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
- NSString *sdkVersion = semanticVersion ?: shortVersion;
- _userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@/%@", appName, appVersion, appBuildNumber, MGLAPIClientUserAgentBase, sdkVersion];
-}
-
-#pragma mark - JSON Serialization
-
-- (NSData *)serializedDataForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- return [NSJSONSerialization dataWithJSONObject:events options:0 error:nil];
-}
-
-#pragma mark NSURLSessionDelegate
-
-- (BOOL)evaluateCertificateWithCertificateData:(NSData *)certificateData keyCount:(CFIndex)keyCount serverTrust:(SecTrustRef)serverTrust challenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
- for (int lc = 0; lc < keyCount; lc++) {
- SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
- NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
- if ([remoteCertificateData isEqualToData:certificateData]) {
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- return YES;
- }
- }
- return NO;
-}
-
-- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
-
- if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
- SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
- SecTrustResultType trustResult;
-
- // Validate the certificate chain with the device's trust store anyway this *might* use revocation checking
- SecTrustEvaluate(serverTrust, &trustResult);
-
- BOOL found = NO; // For clarity; we start in a state where the challange has not been completed and no certificate has been found
-
- if (trustResult == kSecTrustResultUnspecified) {
- // Look for a pinned certificate in the server's certificate chain
- CFIndex numKeys = SecTrustGetCertificateCount(serverTrust);
-
- // Check certs in the following order: digicert 2016, digicert 2017, geotrust 2016, geotrust 2017
- found = [self evaluateCertificateWithCertificateData:self.digicertCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.digicertCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
-
- // If challenge can't be completed with any of the above certs, then try the test server if the app is configured to use the test server
- if (!found && _usesTestServer) {
- found = [self evaluateCertificateWithCertificateData:self.testServerCert keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- }
-
- if (!found) {
- // No certificate was found so cancel the connection.
- completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- }
- }
-}
-
-@end
diff --git a/platform/ios/src/MGLLocationManager.h b/platform/ios/src/MGLLocationManager.h
deleted file mode 100644
index ea23801813..0000000000
--- a/platform/ios/src/MGLLocationManager.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-@protocol MGLLocationManagerDelegate;
-
-@interface MGLLocationManager : NSObject <CLLocationManagerDelegate>
-
-@property (nonatomic, weak) id<MGLLocationManagerDelegate> delegate;
-
-- (void)startUpdatingLocation;
-- (void)stopUpdatingLocation;
-
-@end
-
-@protocol MGLLocationManagerDelegate <NSObject>
-
-@optional
-
-- (void)locationManager:(MGLLocationManager *)locationManager didUpdateLocations:(NSArray *)locations;
-- (void)locationManagerDidStartLocationUpdates:(MGLLocationManager *)locationManager;
-- (void)locationManagerBackgroundLocationUpdatesDidTimeout:(MGLLocationManager *)locationManager;
-- (void)locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:(MGLLocationManager *)locationManager;
-- (void)locationManagerDidStopLocationUpdates:(MGLLocationManager *)locationManager;
-
-@end
diff --git a/platform/ios/src/MGLLocationManager.m b/platform/ios/src/MGLLocationManager.m
deleted file mode 100644
index 85ef4ca489..0000000000
--- a/platform/ios/src/MGLLocationManager.m
+++ /dev/null
@@ -1,175 +0,0 @@
-#import "MGLLocationManager.h"
-#import "MGLTelemetryConfig.h"
-#import <UIKit/UIKit.h>
-
-static const NSTimeInterval MGLLocationManagerHibernationTimeout = 300.0;
-static const NSTimeInterval MGLLocationManagerHibernationPollInterval = 5.0;
-static const CLLocationDistance MGLLocationManagerDistanceFilter = 5.0;
-static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManagerRegionIdentifier.fence.center";
-
-@interface MGLLocationManager ()
-
-@property (nonatomic) CLLocationManager *standardLocationManager;
-@property (nonatomic) BOOL hostAppHasBackgroundCapability;
-@property (nonatomic, getter=isUpdatingLocation) BOOL updatingLocation;
-@property (nonatomic) NSDate *backgroundLocationServiceTimeoutAllowedDate;
-@property (nonatomic) NSTimer *backgroundLocationServiceTimeoutTimer;
-
-@end
-
-@implementation MGLLocationManager
-
-- (instancetype)init {
- self = [super init];
- if (self) {
- NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
- _hostAppHasBackgroundCapability = [backgroundModes containsObject:@"location"];
- }
- return self;
-}
-
-- (void)startUpdatingLocation {
- if ([self isUpdatingLocation]) {
- return;
- }
-
- [self configurePassiveStandardLocationManager];
- [self startLocationServices];
-}
-
-- (void)stopUpdatingLocation {
- if ([self isUpdatingLocation]) {
- [self.standardLocationManager stopUpdatingLocation];
- [self.standardLocationManager stopMonitoringSignificantLocationChanges];
- self.updatingLocation = NO;
- if ([self.delegate respondsToSelector:@selector(locationManagerDidStopLocationUpdates:)]) {
- [self.delegate locationManagerDidStopLocationUpdates:self];
- }
- }
- if(self.standardLocationManager.monitoredRegions.count > 0) {
- for(CLRegion *region in self.standardLocationManager.monitoredRegions) {
- if([region.identifier isEqualToString:MGLLocationManagerRegionIdentifier]) {
- [self.standardLocationManager stopMonitoringForRegion:region];
- }
- }
- }
-}
-
-#pragma mark - Utilities
-
-- (void)configurePassiveStandardLocationManager {
- if (!self.standardLocationManager) {
- CLLocationManager *standardLocationManager = [[CLLocationManager alloc] init];
- standardLocationManager.delegate = self;
- standardLocationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
- standardLocationManager.distanceFilter = MGLLocationManagerDistanceFilter;
- self.standardLocationManager = standardLocationManager;
- }
-}
-
-- (void)startLocationServices {
- CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
- BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorizedAlways;
-#else
- BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorized;
-#endif
- if (authorizedAlways || authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse) {
- // If the host app can run in the background with `always` location permissions then allow background
- // updates and start the significant location change service and background timeout timer
- if (self.hostAppHasBackgroundCapability && authorizedAlways) {
- [self.standardLocationManager startMonitoringSignificantLocationChanges];
- [self startBackgroundTimeoutTimer];
- // On iOS 9 and above also allow background location updates
- if ([self.standardLocationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) {
- self.standardLocationManager.allowsBackgroundLocationUpdates = YES;
- }
- }
-
- [self.standardLocationManager startUpdatingLocation];
- self.updatingLocation = YES;
- if ([self.delegate respondsToSelector:@selector(locationManagerDidStartLocationUpdates:)]) {
- [self.delegate locationManagerDidStartLocationUpdates:self];
- }
- }
-}
-
-- (void)timeoutAllowedCheck {
- if (self.backgroundLocationServiceTimeoutAllowedDate == nil) {
- return;
- }
-
- if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive ||
- [UIApplication sharedApplication].applicationState == UIApplicationStateInactive ) {
- [self startBackgroundTimeoutTimer];
- return;
- }
-
- NSTimeInterval timeIntervalSinceTimeoutAllowed = [[NSDate date] timeIntervalSinceDate:self.backgroundLocationServiceTimeoutAllowedDate];
- if (timeIntervalSinceTimeoutAllowed > 0) {
- [self.standardLocationManager stopUpdatingLocation];
- self.backgroundLocationServiceTimeoutAllowedDate = nil;
- if ([self.delegate respondsToSelector:@selector(locationManagerBackgroundLocationUpdatesDidTimeout:)]) {
- [self.delegate locationManagerBackgroundLocationUpdatesDidTimeout:self];
- }
- }
-}
-
-- (void)startBackgroundTimeoutTimer {
- [self.backgroundLocationServiceTimeoutTimer invalidate];
- self.backgroundLocationServiceTimeoutAllowedDate = [[NSDate date] dateByAddingTimeInterval:MGLLocationManagerHibernationTimeout];
- self.backgroundLocationServiceTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:MGLLocationManagerHibernationPollInterval target:self selector:@selector(timeoutAllowedCheck) userInfo:nil repeats:YES];
-}
-
-- (void)establishRegionMonitoringForLocation:(CLLocation *)location {
- CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius identifier:MGLLocationManagerRegionIdentifier];
- region.notifyOnEntry = NO;
- region.notifyOnExit = YES;
- [self.standardLocationManager startMonitoringForRegion:region];
-}
-
-#pragma mark - CLLocationManagerDelegate
-
-- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
- switch (status) {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
- case kCLAuthorizationStatusAuthorizedAlways:
-#else
- case kCLAuthorizationStatusAuthorized:
-#endif
- case kCLAuthorizationStatusAuthorizedWhenInUse:
- [self startUpdatingLocation];
- break;
- default:
- [self stopUpdatingLocation];
- break;
- }
-}
-
-- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
- CLLocation *location = locations.lastObject;
- if (location.speed > 0.0) {
- [self startBackgroundTimeoutTimer];
- }
- if (self.standardLocationManager.monitoredRegions.count == 0 || location.horizontalAccuracy < MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius) {
- [self establishRegionMonitoringForLocation:location];
- }
- if ([self.delegate respondsToSelector:@selector(locationManager:didUpdateLocations:)]) {
- [self.delegate locationManager:self didUpdateLocations:locations];
- }
-}
-
-- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
- [self startBackgroundTimeoutTimer];
- [self.standardLocationManager startUpdatingLocation];
-}
-
-- (void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager {
- if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
- if ([self.delegate respondsToSelector:@selector(locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:)]) {
- [self.delegate locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:self];
- }
- }
-}
-
-@end
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 72fa4613fc..927450b511 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -66,6 +66,7 @@
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
#import "MGLMapboxEvents.h"
+#import "MMEConstants.h"
#import "MGLSDKUpdateChecker.h"
#import "MGLCompactCalloutView.h"
#import "MGLAnnotationContainerView.h"
@@ -567,7 +568,8 @@ public:
_targetCoordinate = kCLLocationCoordinate2DInvalid;
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
- [MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{}];
+ [MGLMapboxEvents pushTurnstileEvent];
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapLoad withAttributes:@{}];
}
}
@@ -1219,7 +1221,8 @@ public:
[self validateLocationServices];
- [MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{}];
+ [MGLMapboxEvents pushTurnstileEvent];
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapLoad withAttributes:@{}];
}
}
@@ -1331,7 +1334,7 @@ public:
if (pan.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
+ [self trackGestureEvent:MMEEventGesturePanStart forRecognizer:pan];
self.userTrackingMode = MGLUserTrackingModeNone;
@@ -1379,10 +1382,10 @@ public:
CLLocationCoordinate2D panCoordinate = [self convertPoint:pointInView toCoordinateFromView:pan.view];
int zoom = round([self zoomLevel]);
- [MGLMapboxEvents pushEvent:MGLEventTypeMapDragEnd withAttributes:@{
- MGLEventKeyLatitude: @(panCoordinate.latitude),
- MGLEventKeyLongitude: @(panCoordinate.longitude),
- MGLEventKeyZoomLevel: @(zoom)
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapDragEnd withAttributes:@{
+ MMEEventKeyLatitude: @(panCoordinate.latitude),
+ MMEEventKeyLongitude: @(panCoordinate.longitude),
+ MMEEventKeyZoomLevel: @(zoom)
}];
}
@@ -1401,7 +1404,7 @@ public:
if (pinch.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
+ [self trackGestureEvent:MMEEventGesturePinchStart forRecognizer:pinch];
self.scale = powf(2, _mbglMap->getZoom());
@@ -1500,7 +1503,7 @@ public:
if (rotate.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
+ [self trackGestureEvent:MMEEventGestureRotateStart forRecognizer:rotate];
self.angle = MGLRadiansFromDegrees(_mbglMap->getBearing()) * -1;
@@ -1572,7 +1575,7 @@ public:
{
return;
}
- [self trackGestureEvent:MGLEventGestureSingleTap forRecognizer:singleTap];
+ [self trackGestureEvent:MMEEventGestureSingleTap forRecognizer:singleTap];
if (self.mapViewProxyAccessibilityElement.accessibilityElementIsFocused)
{
@@ -1694,7 +1697,7 @@ public:
if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- [self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
+ [self trackGestureEvent:MMEEventGestureDoubleTap forRecognizer:doubleTap];
mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
_mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration));
@@ -1723,7 +1726,7 @@ public:
if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
+ [self trackGestureEvent:MMEEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
[self notifyGestureDidBegin];
}
@@ -1762,7 +1765,7 @@ public:
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
+ [self trackGestureEvent:MMEEventGestureQuickZoom forRecognizer:quickZoom];
self.scale = powf(2, _mbglMap->getZoom());
@@ -1807,7 +1810,7 @@ public:
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
+ [self trackGestureEvent:MMEEventGesturePitchStart forRecognizer:twoFingerDrag];
[self notifyGestureDidBegin];
}
@@ -2012,11 +2015,11 @@ public:
CLLocationCoordinate2D gestureCoordinate = [self convertPoint:pointInView toCoordinateFromView:recognizer.view];
int zoom = round([self zoomLevel]);
- [MGLMapboxEvents pushEvent:MGLEventTypeMapTap withAttributes:@{
- MGLEventKeyLatitude: @(gestureCoordinate.latitude),
- MGLEventKeyLongitude: @(gestureCoordinate.longitude),
- MGLEventKeyZoomLevel: @(zoom),
- MGLEventKeyGestureID: gestureID
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapTap withAttributes:@{
+ MMEEventKeyLatitude: @(gestureCoordinate.latitude),
+ MMEEventKeyLongitude: @(gestureCoordinate.longitude),
+ MMEEventKeyZoomLevel: @(zoom),
+ MMEEventKeyGestureID: gestureID
}];
}
diff --git a/platform/ios/src/MGLMapboxEvents.h b/platform/ios/src/MGLMapboxEvents.h
index 98f59ffd3f..cbac578798 100644
--- a/platform/ios/src/MGLMapboxEvents.h
+++ b/platform/ios/src/MGLMapboxEvents.h
@@ -1,44 +1,17 @@
#import <Foundation/Foundation.h>
-
-#import "MGLTypes.h"
+#import "MMEEventsManager.h"
NS_ASSUME_NONNULL_BEGIN
-// Event types
-extern NSString *const MGLEventTypeAppUserTurnstile;
-extern NSString *const MGLEventTypeMapLoad;
-extern NSString *const MGLEventTypeMapTap;
-extern NSString *const MGLEventTypeMapDragEnd;
-extern NSString *const MGLEventTypeLocation;
-
-// Event keys
-extern NSString *const MGLEventKeyLatitude;
-extern NSString *const MGLEventKeyLongitude;
-extern NSString *const MGLEventKeyZoomLevel;
-extern NSString *const MGLEventKeyGestureID;
-
-// Gestures
-extern NSString *const MGLEventGestureSingleTap;
-extern NSString *const MGLEventGestureDoubleTap;
-extern NSString *const MGLEventGestureTwoFingerSingleTap;
-extern NSString *const MGLEventGestureQuickZoom;
-extern NSString *const MGLEventGesturePanStart;
-extern NSString *const MGLEventGesturePinchStart;
-extern NSString *const MGLEventGestureRotateStart;
-extern NSString *const MGLEventGesturePitchStart;
-
-typedef NS_DICTIONARY_OF(NSString *, id) MGLMapboxEventAttributes;
-typedef NS_MUTABLE_DICTIONARY_OF(NSString *, id) MGLMutableMapboxEventAttributes;
-
@interface MGLMapboxEvents : NSObject
-+ (nullable instancetype)sharedManager;
++ (nullable instancetype)sharedInstance;
-// You must call these methods from the main thread.
-//
-+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary;
-+ (void)ensureMetricsOptoutExists;
++ (void)setupWithAccessToken:(NSString *)accessToken;
++ (void)pushTurnstileEvent;
++ (void)pushEvent:(NSString *)event withAttributes:(MMEMapboxEventAttributes *)attributeDictionary;
+ (void)flush;
++ (void)ensureMetricsOptoutExists;
@end
diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m
index 273af5b3bc..05f291d8a0 100644
--- a/platform/ios/src/MGLMapboxEvents.m
+++ b/platform/ios/src/MGLMapboxEvents.m
@@ -1,646 +1,166 @@
#import "MGLMapboxEvents.h"
-#import <UIKit/UIKit.h>
-#import <CoreLocation/CoreLocation.h>
-#import "MGLAccountManager.h"
+#import "NSBundle+MGLAdditions.h"
#import "NSProcessInfo+MGLAdditions.h"
-#import "NSException+MGLAdditions.h"
-#import "MGLAPIClient.h"
-#import "MGLLocationManager.h"
-#import "MGLTelemetryConfig.h"
-#include <mbgl/storage/reachability.h>
-#include <sys/sysctl.h>
+static NSString * const MGLAPIClientUserAgentBase = @"mapbox-maps-ios";
+static NSString * const MGLMapboxAccountType = @"MGLMapboxAccountType";
+static NSString * const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled";
+static NSString * const MGLMapboxMetricsDebugLoggingEnabled = @"MGLMapboxMetricsDebugLoggingEnabled";
+static NSString * const MGLTelemetryAccessToken = @"MGLTelemetryAccessToken";
+static NSString * const MGLTelemetryBaseURL = @"MGLTelemetryBaseURL";
-// Event types
-NSString *const MGLEventTypeAppUserTurnstile = @"appUserTurnstile";
-NSString *const MGLEventTypeMapLoad = @"map.load";
-NSString *const MGLEventTypeMapTap = @"map.click";
-NSString *const MGLEventTypeMapDragEnd = @"map.dragend";
-NSString *const MGLEventTypeLocation = @"location";
-NSString *const MGLEventTypeLocalDebug = @"debug";
+@interface MGLMapboxEvents ()
-// Gestures
-NSString *const MGLEventGestureSingleTap = @"SingleTap";
-NSString *const MGLEventGestureDoubleTap = @"DoubleTap";
-NSString *const MGLEventGestureTwoFingerSingleTap = @"TwoFingerTap";
-NSString *const MGLEventGestureQuickZoom = @"QuickZoom";
-NSString *const MGLEventGesturePanStart = @"Pan";
-NSString *const MGLEventGesturePinchStart = @"Pinch";
-NSString *const MGLEventGestureRotateStart = @"Rotation";
-NSString *const MGLEventGesturePitchStart = @"Pitch";
-
-// Event keys
-NSString *const MGLEventKeyLatitude = @"lat";
-NSString *const MGLEventKeyLongitude = @"lng";
-NSString *const MGLEventKeyZoomLevel = @"zoom";
-NSString *const MGLEventKeySpeed = @"speed";
-NSString *const MGLEventKeyCourse = @"course";
-NSString *const MGLEventKeyGestureID = @"gesture";
-NSString *const MGLEventHorizontalAccuracy = @"horizontalAccuracy";
-NSString *const MGLEventKeyLocalDebugDescription = @"debug.description";
-
-static NSString *const MGLEventKeyEvent = @"event";
-static NSString *const MGLEventKeyCreated = @"created";
-static NSString *const MGLEventKeyVendorID = @"userId";
-static NSString *const MGLEventKeyModel = @"model";
-static NSString *const MGLEventKeyEnabledTelemetry = @"enabled.telemetry";
-static NSString *const MGLEventKeyOperatingSystem = @"operatingSystem";
-static NSString *const MGLEventKeyResolution = @"resolution";
-static NSString *const MGLEventKeyAccessibilityFontScale = @"accessibilityFontScale";
-static NSString *const MGLEventKeyOrientation = @"orientation";
-static NSString *const MGLEventKeyPluggedIn = @"pluggedIn";
-static NSString *const MGLEventKeyWifi = @"wifi";
-static NSString *const MGLEventKeySource = @"source";
-static NSString *const MGLEventKeySessionId = @"sessionId";
-static NSString *const MGLEventKeyApplicationState = @"applicationState";
-static NSString *const MGLEventKeyAltitude = @"altitude";
-
-static NSString *const MGLMapboxAccountType = @"MGLMapboxAccountType";
-static NSString *const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled";
-
-// SDK event source
-static NSString *const MGLEventSource = @"mapbox";
-
-// Event application state
-static NSString *const MGLApplicationStateForeground = @"Foreground";
-static NSString *const MGLApplicationStateBackground = @"Background";
-static NSString *const MGLApplicationStateInactive = @"Inactive";
-static NSString *const MGLApplicationStateUnknown = @"Unknown";
-
-const NSUInteger MGLMaximumEventsPerFlush = 180;
-const NSTimeInterval MGLFlushInterval = 180;
-
-@interface MGLMapboxEventsData : NSObject
-
-@property (nonatomic) NSString *vendorId;
-@property (nonatomic) NSString *model;
-@property (nonatomic) NSString *iOSVersion;
-@property (nonatomic) CGFloat scale;
-
-@end
-
-@implementation MGLMapboxEventsData
-
-- (instancetype)init {
- if (self = [super init]) {
- _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;
- }
- }
- return self;
-}
-
-- (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;
-}
+@property (nonatomic) MMEEventsManager *eventsManager;
+@property (nonatomic) NSURL *baseURL;
+@property (nonatomic, copy) NSString *accessToken;
@end
+@implementation MGLMapboxEvents
-@interface MGLMapboxEvents () <MGLLocationManagerDelegate>
-
-@property (nonatomic) MGLMapboxEventsData *data;
-@property (nonatomic, copy) NSString *appBundleId;
-@property (nonatomic, readonly) NSString *instanceID;
-@property (nonatomic, copy) NSString *dateForDebugLogFile;
-@property (nonatomic) NSDateFormatter *rfc3339DateFormatter;
-@property (nonatomic) MGLAPIClient *apiClient;
-@property (nonatomic) BOOL usesTestServer;
-@property (nonatomic) BOOL canEnableDebugLogging;
-@property (nonatomic, getter=isPaused) BOOL paused;
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLMapboxEventAttributes *) *eventQueue;
-@property (nonatomic) dispatch_queue_t serialQueue;
-@property (nonatomic) dispatch_queue_t debugLogSerialQueue;
-@property (nonatomic) MGLLocationManager *locationManager;
-@property (nonatomic) NSTimer *timer;
-@property (nonatomic) NSDate *instanceIDRotationDate;
-@property (nonatomic) NSDate *nextTurnstileSendDate;
-@property (nonatomic) NSNumber *currentAccountTypeValue;
-@property (nonatomic) BOOL currentMetricsEnabledValue;
-
-@end
-
-@implementation MGLMapboxEvents {
- NSString *_instanceID;
- UIBackgroundTaskIdentifier _backgroundTaskIdentifier;
-}
+ (void)initialize {
if (self == [MGLMapboxEvents class]) {
NSBundle *bundle = [NSBundle mainBundle];
NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:MGLMapboxAccountType];
- [[NSUserDefaults standardUserDefaults] registerDefaults:@{
- MGLMapboxAccountType: accountTypeNumber ?: @0,
- MGLMapboxMetricsEnabled: @YES,
- @"MGLMapboxMetricsDebugLoggingEnabled": @NO,
- }];
+ [[NSUserDefaults standardUserDefaults] registerDefaults:@{MGLMapboxAccountType: accountTypeNumber ?: @0,
+ MGLMapboxMetricsEnabled: @YES,
+ MGLMapboxMetricsDebugLoggingEnabled: @NO}];
}
}
-+ (BOOL)isEnabled {
-#if TARGET_OS_SIMULATOR
- return NO;
-#else
- BOOL isLowPowerModeEnabled = NO;
- if ([NSProcessInfo instancesRespondToSelector:@selector(isLowPowerModeEnabled)]) {
- isLowPowerModeEnabled = [[NSProcessInfo processInfo] isLowPowerModeEnabled];
++ (nullable instancetype)sharedInstance {
+ if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
+ return nil;
}
- return ([[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled] &&
- [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0 &&
- !isLowPowerModeEnabled);
-#endif
-}
-
-
-- (BOOL)debugLoggingEnabled {
- return (self.canEnableDebugLogging &&
- [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]);
+
+ static dispatch_once_t onceToken;
+ static MGLMapboxEvents *_sharedInstance;
+ dispatch_once(&onceToken, ^{
+ _sharedInstance = [[self alloc] init];
+ });
+ return _sharedInstance;
}
-- (instancetype) init {
+- (instancetype)init {
self = [super init];
if (self) {
- [MGLTelemetryConfig.sharedConfig configurationFromKey:[[NSUserDefaults standardUserDefaults] objectForKey:MGLMapboxMetricsProfile]];
+ _eventsManager = [[MMEEventsManager alloc] init];
+ _eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabled];
+ _eventsManager.accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType];
+ _eventsManager.metricsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled];
- _currentAccountTypeValue = @0;
- _currentMetricsEnabledValue = YES;
-
- _appBundleId = [[NSBundle mainBundle] bundleIdentifier];
- _apiClient = [[MGLAPIClient alloc] init];
-
- NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
- _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
-
- _locationManager = [[MGLLocationManager alloc] init];
- _locationManager.delegate = self;
- _paused = YES;
- [self resumeMetricsCollection];
-
- // Events Control
- _eventQueue = [[NSMutableArray alloc] init];
-
- // Setup Date Format
- _rfc3339DateFormatter = [[NSDateFormatter alloc] init];
- NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
-
- [_rfc3339DateFormatter setLocale:enUSPOSIXLocale];
- [_rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
- // Clear Any System TimeZone Cache
- [NSTimeZone resetSystemTimeZone];
- [_rfc3339DateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
-
- // Configure logging
- if ([self isProbablyAppStoreBuild]) {
- self.canEnableDebugLogging = NO;
-
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]) {
- NSLog(@"Telemetry logging is only enabled in non-app store builds.");
- }
- } else {
- self.canEnableDebugLogging = YES;
+ // It is possible for the shared instance of this class to be created because of a call to
+ // +[MGLAccountManager load] early on in the app lifecycle of the host application.
+ // If user default values for access token and base URL are available, they are stored here
+ // on local properties so that they can be applied later once MMEEventsManager is fully initialized
+ // (once -[MMEEventsManager initializeWithAccessToken:userAgentBase:hostSDKVersion:] is called.
+ // Normally, the telem access token and base URL are not set this way. However, overriding these values
+ // with user defaults can be useful for testing with an alternative (test) backend system.
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) {
+ self.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken];
}
-
- // Watch for changes to telemetry settings by the user
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:UIApplicationDidBecomeActiveNotification object:nil];
-
- // Watch for Low Power Mode change events
- if (&NSProcessInfoPowerStateDidChangeNotification != NULL) {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:NSProcessInfoPowerStateDidChangeNotification object:nil];
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) {
+ self.baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]];
}
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil];
}
return self;
}
-// Called implicitly from any public class convenience methods.
-// May return nil if this feature is disabled.
-//
-+ (nullable instancetype)sharedManager {
- if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
- return nil;
- }
- static dispatch_once_t onceToken;
- static MGLMapboxEvents *_sharedManager;
- dispatch_once(&onceToken, ^{
- _sharedManager = [[self alloc] init];
- });
- return _sharedManager;
-}
-
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
- [self pauseMetricsCollection];
}
-- (NSString *)instanceID {
- if (self.instanceIDRotationDate && [[NSDate date] timeIntervalSinceDate:self.instanceIDRotationDate] >= 0) {
- _instanceID = nil;
+- (void)userDefaultsDidChange:(NSNotification *)notification {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self updateNonDisablingConfigurationValues];
+ [self updateDisablingConfigurationValuesWithNotification:notification];
+ });
+}
+
+- (void)updateNonDisablingConfigurationValues {
+ self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
+
+ // It is possible for `MGLTelemetryAccessToken` 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:MGLTelemetryAccessToken]) {
+ self.eventsManager.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken];
}
- if (!_instanceID) {
- _instanceID = [[NSUUID UUID] UUIDString];
- NSTimeInterval twentyFourHourTimeInterval = 24 * 3600;
- self.instanceIDRotationDate = [[NSDate date] dateByAddingTimeInterval:twentyFourHourTimeInterval];
+
+ // It is possible for `MGLTelemetryBaseURL` to have been set yet `userDefaultsDidChange:`
+ // is called before setupWithAccessToken: is called.
+ // In that case, setting the base URL here will have no effect. In practice, that's fine
+ // because the base URL value will be resolved when `setupWithAccessToken:` is called eventually
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) {
+ NSURL *baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]];
+ self.eventsManager.baseURL = baseURL;
}
- return _instanceID;
}
-- (void)userDefaultsDidChange:(NSNotification *)notification {
-
+- (void)updateDisablingConfigurationValuesWithNotification:(NSNotification *)notification {
// Guard against over calling pause / resume if the values this implementation actually
- // cares about have not changed
-
+ // 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];
- NSNumber *accountType = [userDefaults objectForKey:MGLMapboxAccountType];
- BOOL metricsEnabled = [[userDefaults objectForKey:MGLMapboxMetricsEnabled] boolValue];
-
- if (![accountType isEqualToNumber:self.currentAccountTypeValue] || metricsEnabled != self.currentMetricsEnabledValue) {
- [self pauseOrResumeMetricsCollectionIfRequired];
- self.currentAccountTypeValue = accountType;
- self.currentMetricsEnabledValue = metricsEnabled;
- }
- }
-
-}
-
-- (void)pauseOrResumeMetricsCollectionIfRequired {
-
- // [CLLocationManager authorizationStatus] has been found to block in some cases so
- // dispatch the call to a non-UI thread
- dispatch_async(self.serialQueue, ^{
- CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
+ NSInteger accountType = [userDefaults integerForKey:MGLMapboxAccountType];
+ BOOL metricsEnabled = [userDefaults boolForKey:MGLMapboxMetricsEnabled];
- // Checking application state must be done on the main thread for safety and
- // to avoid a thread sanitizer error
- dispatch_async(dispatch_get_main_queue(), ^{
- UIApplication *application = [UIApplication sharedApplication];
- UIApplicationState state = application.applicationState;
-
- // Prevent blue status bar when host app has `when in use` permission only and it is not in foreground
- if (status == kCLAuthorizationStatusAuthorizedWhenInUse && state == UIApplicationStateBackground) {
- if (_backgroundTaskIdentifier == UIBackgroundTaskInvalid) {
- _backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
- [application endBackgroundTask:_backgroundTaskIdentifier];
- _backgroundTaskIdentifier = UIBackgroundTaskInvalid;
- }];
- [self flush];
- }
- [self pauseMetricsCollection];
- return;
- }
+ if (accountType != self.eventsManager.accountType || metricsEnabled != self.eventsManager.metricsEnabled) {
+ self.eventsManager.accountType = accountType;
+ self.eventsManager.metricsEnabled = metricsEnabled;
- // Toggle pause based on current pause state, user opt-out state, and low-power state.
- BOOL enabled = [[self class] isEnabled];
- if (self.paused && enabled) {
- [self resumeMetricsCollection];
- } else if (!self.paused && !enabled) {
- [self flush];
- [self pauseMetricsCollection];
- }
- });
- });
-}
-
-- (void)pauseMetricsCollection {
- if (self.paused) {
- return;
- }
-
- self.paused = YES;
- [self.timer invalidate];
- self.timer = nil;
- [self.eventQueue removeAllObjects];
- self.data = nil;
-
- [self.locationManager stopUpdatingLocation];
-}
-
-- (void)resumeMetricsCollection {
- if (!self.paused || ![[self class] isEnabled]) {
- return;
- }
-
- self.paused = NO;
- self.data = [[MGLMapboxEventsData alloc] init];
-
- [self.locationManager startUpdatingLocation];
-}
-
-+ (void)flush {
- [[MGLMapboxEvents sharedManager] flush];
-}
-
-- (void)flush {
- if ([MGLAccountManager accessToken] == nil) {
- return;
- }
-
- NSArray *events = [NSArray arrayWithArray:self.eventQueue];
- [self.eventQueue removeAllObjects];
-
- [self postEvents:events];
-
- if (self.timer) {
- [self.timer invalidate];
- self.timer = nil;
- }
-
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"flush"}];
-}
-
-- (void)pushTurnstileEvent {
- if (self.nextTurnstileSendDate && [[NSDate date] timeIntervalSinceDate:self.nextTurnstileSendDate] < 0) {
- return;
- }
-
- NSString *vendorID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
- if (!vendorID) {
- return;
- }
-
- NSDictionary *turnstileEventAttributes = @{MGLEventKeyEvent: MGLEventTypeAppUserTurnstile,
- MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]],
- MGLEventKeyVendorID: vendorID,
- MGLEventKeyEnabledTelemetry: @([[self class] isEnabled])};
-
- if ([MGLAccountManager accessToken] == nil) {
- return;
- }
-
- __weak __typeof__(self) weakSelf = self;
- [self.apiClient postEvent:turnstileEventAttributes completionHandler:^(NSError * _Nullable error) {
- __strong __typeof__(weakSelf) strongSelf = weakSelf;
- if (error) {
- [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Network error",
- @"error": error}];
- return;
+ [self.eventsManager pauseOrResumeMetricsCollectionIfRequired];
}
- [strongSelf writeEventToLocalDebugLog:turnstileEventAttributes];
- [strongSelf updateNextTurnstileSendDate];
- }];
-}
-
-- (void)updateNextTurnstileSendDate {
- // Find the time a day from now (sometime tomorrow)
- NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
- NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
- dayComponent.day = 1;
- NSDate *sometimeTomorrow = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];
-
- // Find the start of tomorrow and use that as the next turnstile send date. The effect of this is that
- // turnstile events can be sent as much as once per calendar day and always at the start of a session
- // when a map load happens.
- NSDate *startOfTomorrow = nil;
- [calendar rangeOfUnit:NSCalendarUnitDay startDate:&startOfTomorrow interval:nil forDate:sometimeTomorrow];
- self.nextTurnstileSendDate = startOfTomorrow;
-}
-
-+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- [[MGLMapboxEvents sharedManager] pushEvent:event withAttributes:attributeDictionary];
-}
-
-- (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- if (!event) {
- return;
- }
-
- if ([event isEqualToString:MGLEventTypeMapLoad]) {
- [self pushTurnstileEvent];
- }
-
- if (self.paused) {
- return;
- }
-
- MGLMapboxEventAttributes *fullyFormedEvent = [self fullyFormedEventForEvent:event withAttributes:attributeDictionary];
- if (fullyFormedEvent) {
- [self.eventQueue addObject:fullyFormedEvent];
- [self writeEventToLocalDebugLog:fullyFormedEvent];
- // Has Flush Limit Been Reached?
- if (self.eventQueue.count >= MGLMaximumEventsPerFlush) {
- [self flush];
- } else if (self.eventQueue.count == 1) {
- // If this is first new event on queue start timer,
- [self startTimer];
- }
- } else {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Unknown event",
- @"eventName": event,
- @"event.attributes": attributeDictionary}];
- }
-}
-
-#pragma mark Events
-
-- (MGLMapboxEventAttributes *)fullyFormedEventForEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- if ([event isEqualToString:MGLEventTypeMapLoad]) {
- return [self mapLoadEventWithAttributes:attributeDictionary];
- } else if ([event isEqualToString:MGLEventTypeMapTap]) {
- return [self mapClickEventWithAttributes:attributeDictionary];
- } else if ([event isEqualToString:MGLEventTypeMapDragEnd]) {
- return [self mapDragEndEventWithAttributes:attributeDictionary];
- } else if ([event isEqualToString:MGLEventTypeLocation]) {
- return [self locationEventWithAttributes:attributeDictionary];
}
- return nil;
-}
-
-- (MGLMapboxEventAttributes *)locationEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
- attributes[MGLEventKeyEvent] = MGLEventTypeLocation;
- attributes[MGLEventKeySource] = MGLEventSource;
- attributes[MGLEventKeySessionId] = self.instanceID;
- attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion;
- NSString *currentApplicationState = [self applicationState];
- if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) {
- attributes[MGLEventKeyApplicationState] = currentApplicationState;
- }
-
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
-}
-
-- (MGLMapboxEventAttributes *)mapLoadEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
- attributes[MGLEventKeyEvent] = MGLEventTypeMapLoad;
- attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]];
- attributes[MGLEventKeyVendorID] = self.data.vendorId;
- attributes[MGLEventKeyModel] = self.data.model;
- attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion;
- attributes[MGLEventKeyResolution] = @(self.data.scale);
- attributes[MGLEventKeyAccessibilityFontScale] = @([self contentSizeScale]);
- attributes[MGLEventKeyOrientation] = [self deviceOrientation];
- attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]);
-
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
-}
-
-- (MGLMapboxEventAttributes *)mapClickEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [self interactionEvent];
- attributes[MGLEventKeyEvent] = MGLEventTypeMapTap;
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
-}
-
-- (MGLMapboxEventAttributes *)mapDragEndEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [self interactionEvent];
- attributes[MGLEventKeyEvent] = MGLEventTypeMapDragEnd;
-
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
}
-- (MGLMutableMapboxEventAttributes *)interactionEvent {
- MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
- attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]];
- attributes[MGLEventKeyOrientation] = [self deviceOrientation];
- attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]);
-
- return attributes;
-}
-
-- (MGLMapboxEventAttributes *)eventForAttributes:(MGLMutableMapboxEventAttributes *)attributes attributeDictionary:(MGLMapboxEventAttributes *)attributeDictionary {
- [attributes addEntriesFromDictionary:attributeDictionary];
-
- return [attributes copy];
-}
-
-// Called implicitly from public use of +flush.
-//
-- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- if (self.paused) {
- return;
++ (void)setupWithAccessToken:(NSString *)accessToken {
+ NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
+ NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
+ NSString *sdkVersion = semanticVersion ?: shortVersion;
+
+ // It is possible that an alternative access token was already set on this instance when the class was loaded
+ // Use it if it exists
+ NSString *resolvedAccessToken = [MGLMapboxEvents sharedInstance].accessToken ?: accessToken;
+
+ [[[self sharedInstance] eventsManager] initializeWithAccessToken:resolvedAccessToken userAgentBase:MGLAPIClientUserAgentBase hostSDKVersion:sdkVersion];
+
+ // It is possible that an alternative base URL was set on this instance when the class was loaded
+ // Use it if it exists
+ if ([MGLMapboxEvents sharedInstance].baseURL) {
+ [[MGLMapboxEvents sharedInstance] eventsManager].baseURL = [MGLMapboxEvents sharedInstance].baseURL;
}
-
- __weak __typeof__(self) weakSelf = self;
- dispatch_async(self.serialQueue, ^{
- __strong __typeof__(weakSelf) strongSelf = weakSelf;
- [self.apiClient postEvents:events completionHandler:^(NSError * _Nullable error) {
- if (error) {
- [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Network error",
- @"error": error}];
- } else {
- [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"post",
- @"debug.eventsCount": @(events.count)}];
- }
- [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
- _backgroundTaskIdentifier = UIBackgroundTaskInvalid;
- }];
- });
-}
-
-- (void)startTimer {
- [self.timer invalidate];
- self.timer = [NSTimer scheduledTimerWithTimeInterval:MGLFlushInterval
- target:self
- selector:@selector(flush)
- userInfo:nil
- repeats:YES];
}
-- (NSString *)deviceOrientation {
- NSString *result;
-
- 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;
++ (void)pushTurnstileEvent {
+ [[[self sharedInstance] eventsManager] sendTurnstileEvent];
}
-- (NSString *)applicationState {
- switch ([UIApplication sharedApplication].applicationState) {
- case UIApplicationStateActive:
- return MGLApplicationStateForeground;
- case UIApplicationStateInactive:
- return MGLApplicationStateInactive;
- case UIApplicationStateBackground:
- return MGLApplicationStateBackground;
- default:
- return MGLApplicationStateUnknown;
- }
++ (void)pushEvent:(NSString *)event withAttributes:(MMEMapboxEventAttributes *)attributeDictionary {
+ [[[self sharedInstance] eventsManager] enqueueEventWithName:event attributes:attributeDictionary];
}
-- (NSInteger)contentSizeScale {
- NSInteger result = -9999;
-
- 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;
++ (void)flush {
+ [[[self sharedInstance] eventsManager] flush];
}
+ (void)ensureMetricsOptoutExists {
NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"];
BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue];
-
+
if (!metricsEnabledSettingShownInAppFlag &&
[[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0) {
// Opt-out is not configured in UI, so check for Settings.bundle
id defaultEnabledValue;
NSString *appSettingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
-
+
if (appSettingsBundle) {
// Dynamic Settings.bundle loading based on http://stackoverflow.com/a/510329/2094275
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[appSettingsBundle stringByAppendingPathComponent:@"Root.plist"]];
@@ -651,7 +171,7 @@ const NSTimeInterval MGLFlushInterval = 180;
}
}
}
-
+
if (!defaultEnabledValue) {
[NSException raise:@"Telemetry opt-out missing" format:
@"End users must be able to opt out of Mapbox Telemetry in your app, either inside Settings (via Settings.bundle) or inside this app. "
@@ -663,153 +183,4 @@ const NSTimeInterval MGLFlushInterval = 180;
}
}
-#pragma mark CLLocationManagerUtilityDelegate
-
-- (void)locationManager:(MGLLocationManager *)locationManager didUpdateLocations:(NSArray *)locations {
- for (CLLocation *loc in locations) {
- double accuracy = 10000000;
- double lat = floor(loc.coordinate.latitude * accuracy) / accuracy;
- double lng = floor(loc.coordinate.longitude * accuracy) / accuracy;
- double horizontalAccuracy = round(loc.horizontalAccuracy);
- NSString *formattedDate = [self.rfc3339DateFormatter stringFromDate:loc.timestamp];
- [MGLMapboxEvents pushEvent:MGLEventTypeLocation withAttributes:@{MGLEventKeyCreated: formattedDate,
- MGLEventKeyLatitude: @(lat),
- MGLEventKeyLongitude: @(lng),
- MGLEventKeyAltitude: @(round(loc.altitude)),
- MGLEventHorizontalAccuracy: @(horizontalAccuracy)}];
- }
-}
-
-- (void)locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerAutoPause"}];
-}
-
-- (void)locationManagerBackgroundLocationUpdatesDidTimeout:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerTimeout"}];
-}
-
-- (void)locationManagerDidStartLocationUpdates:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerStartUpdates"}];
-}
-
-- (void)locationManagerDidStopLocationUpdates:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"locationManager.locationManagerStopUpdates"}];
-}
-
-#pragma mark MGLMapboxEvents Debug
-
-- (void)pushDebugEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- if (![self debugLoggingEnabled]) {
- return;
- }
-
- if (!event) {
- return;
- }
-
- MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:attributeDictionary];
- [evt setObject:event forKey:@"event"];
- [evt setObject:[self.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
- [evt setValue:[self applicationState] forKey:@"applicationState"];
- [evt setValue:@([[self class] isEnabled]) forKey:@"telemetryEnabled"];
- [evt setObject:self.instanceID forKey:@"instance"];
-
- MGLMapboxEventAttributes *finalEvent = [NSDictionary dictionaryWithDictionary:evt];
- [self writeEventToLocalDebugLog:finalEvent];
-}
-
-- (void)writeEventToLocalDebugLog:(MGLMapboxEventAttributes *)event {
- if (![self debugLoggingEnabled]) {
- return;
- }
-
- NSLog(@"%@", [self stringForDebugEvent:event]);
-
- if (!self.dateForDebugLogFile) {
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
- [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
- self.dateForDebugLogFile = [dateFormatter stringFromDate:[NSDate date]];
- }
-
- if (!self.debugLogSerialQueue) {
- NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
- self.debugLogSerialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.debugLog", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
- }
-
- dispatch_async(self.debugLogSerialQueue, ^{
- if ([NSJSONSerialization isValidJSONObject:event]) {
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:event options:NSJSONWritingPrettyPrinted error:nil];
-
- NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
- jsonString = [jsonString stringByAppendingString:@",\n"];
-
- NSString *logFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", self.dateForDebugLogFile]];
-
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- if ([fileManager fileExistsAtPath:logFilePath]) {
- NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
- [fileHandle seekToEndOfFile];
- [fileHandle writeData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
- } else {
- [fileManager createFileAtPath:logFilePath contents:[jsonString dataUsingEncoding:NSUTF8StringEncoding] attributes:@{ NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication }];
- }
- }
- });
-}
-
-- (NSString *)stringForDebugEvent:(MGLMapboxEventAttributes *)event {
- // redact potentially sensitive location details from system console log
- if ([event[@"event"] isEqualToString:MGLEventTypeLocation]) {
- MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:event];
- [evt setObject:@"<redacted>" forKey:@"lat"];
- [evt setObject:@"<redacted>" forKey:@"lng"];
- event = evt;
- }
-
- return [NSString stringWithFormat:@"Mapbox Telemetry event %@", event];
-}
-
-- (BOOL)isProbablyAppStoreBuild {
-#if TARGET_IPHONE_SIMULATOR
- return NO;
-#else
- // BugshotKit by Marco Arment https://github.com/marcoarment/BugshotKit/
- // Adapted from https://github.com/blindsightcorp/BSMobileProvision
-
- NSString *binaryMobileProvision = [NSString stringWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"] encoding:NSISOLatin1StringEncoding error:NULL];
- if (!binaryMobileProvision) {
- return YES; // no provision
- }
-
- NSScanner *scanner = [NSScanner scannerWithString:binaryMobileProvision];
- NSString *plistString;
- if (![scanner scanUpToString:@"<plist" intoString:nil] || ! [scanner scanUpToString:@"</plist>" intoString:&plistString]) {
- return YES; // no XML plist found in provision
- }
- plistString = [plistString stringByAppendingString:@"</plist>"];
-
- NSData *plistdata_latin1 = [plistString dataUsingEncoding:NSISOLatin1StringEncoding];
- NSError *error = nil;
- NSDictionary *mobileProvision = [NSPropertyListSerialization propertyListWithData:plistdata_latin1 options:NSPropertyListImmutable format:NULL error:&error];
- if (error) {
- return YES; // unknown plist format
- }
-
- if (!mobileProvision || ! mobileProvision.count) {
- return YES; // no entitlements
- }
-
- if (mobileProvision[@"ProvisionsAllDevices"]) {
- return NO; // enterprise provisioning
- }
-
- if (mobileProvision[@"ProvisionedDevices"] && [mobileProvision[@"ProvisionedDevices"] count]) {
- return NO; // development or ad-hoc
- }
-
- return YES; // expected development/enterprise/ad-hoc entitlements not found
-#endif
-}
-
@end
diff --git a/platform/ios/src/Mapbox-Prefix.pch b/platform/ios/src/Mapbox-Prefix.pch
new file mode 100644
index 0000000000..6754020861
--- /dev/null
+++ b/platform/ios/src/Mapbox-Prefix.pch
@@ -0,0 +1 @@
+#import "MMENamespacedDependencies.h"