diff options
Diffstat (limited to 'platform/darwin/src/MGLNetworkConfiguration.mm')
-rw-r--r-- | platform/darwin/src/MGLNetworkConfiguration.mm | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/platform/darwin/src/MGLNetworkConfiguration.mm b/platform/darwin/src/MGLNetworkConfiguration.mm new file mode 100644 index 0000000000..fba78e4e81 --- /dev/null +++ b/platform/darwin/src/MGLNetworkConfiguration.mm @@ -0,0 +1,180 @@ +#import "MGLNetworkConfiguration_Private.h" + +#include <mbgl/storage/reachability.h> +#include <mbgl/storage/network_status.hpp> + +static NSString * const MGLStartTime = @"start_time"; +static NSString * const MGLResourceType = @"resource_type"; +NSString * const kMGLDownloadPerformanceEvent = @"mobile.performance_trace"; + +@interface MGLNetworkConfiguration () + +@property (strong) NSURLSessionConfiguration *sessionConfig; +@property (nonatomic, strong) NSMutableDictionary<NSString *, NSDictionary*> *events; +@property (nonatomic, weak) id<MGLNetworkConfigurationMetricsDelegate> metricsDelegate; +@property (nonatomic) dispatch_queue_t eventsQueue; + +@end + +@implementation MGLNetworkConfiguration + +- (instancetype)init { + if (self = [super init]) { + self.sessionConfiguration = nil; + _events = [NSMutableDictionary dictionary]; + _eventsQueue = dispatch_queue_create("com.mapbox.network-configuration", DISPATCH_QUEUE_CONCURRENT); + } + + return self; +} + ++ (instancetype)sharedManager { + static dispatch_once_t onceToken; + static MGLNetworkConfiguration *_sharedManager; + dispatch_once(&onceToken, ^{ + _sharedManager = [[self alloc] init]; + }); + + return _sharedManager; +} + +- (void)setSessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration { + @synchronized (self) { + if (sessionConfiguration == nil) { + _sessionConfig = [self defaultSessionConfiguration]; + } else { + _sessionConfig = sessionConfiguration; + } + } +} + +- (NSURLSessionConfiguration *)sessionConfiguration { + NSURLSessionConfiguration *sessionConfig = nil; + @synchronized (self) { + sessionConfig = _sessionConfig; + } + return sessionConfig; +} + +- (NSURLSessionConfiguration *)defaultSessionConfiguration { + NSURLSessionConfiguration* sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + + sessionConfiguration.timeoutIntervalForResource = 30; + sessionConfiguration.HTTPMaximumConnectionsPerHost = 8; + sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + sessionConfiguration.URLCache = nil; + + return sessionConfiguration; +} + +- (void)setStopsRequests:(BOOL)stopsRequests { + if (stopsRequests) { + mbgl::NetworkStatus::Set(mbgl::NetworkStatus::Status::Offline); + } else { + mbgl::NetworkStatus::Set(mbgl::NetworkStatus::Status::Online); + } +} + +- (BOOL)stopsRequests { + auto status = mbgl::NetworkStatus::Get(); + if (status == mbgl::NetworkStatus::Status::Offline) { + return YES; + } else { + return NO; + } +} + +- (void)startDownloadEvent:(NSString *)urlString type:(NSString *)resourceType { + if (urlString && ![self eventDictionaryForKey:urlString]) { + NSDate *startDate = [NSDate date]; + [self setEventDictionary:@{ MGLStartTime: startDate, MGLResourceType: resourceType } forKey:urlString]; + } +} + +- (void)stopDownloadEventForResponse:(NSURLResponse *)response { + [self sendEventForURLResponse:response withAction:nil]; +} + +- (void)cancelDownloadEventForResponse:(NSURLResponse *)response { + [self sendEventForURLResponse:response withAction:@"cancel"]; +} + +- (void)sendEventForURLResponse:(NSURLResponse *)response withAction:(NSString *)action +{ + if ([response isKindOfClass:[NSURLResponse class]]) { + NSString *urlString = response.URL.relativePath; + if (urlString && [self eventDictionaryForKey:urlString]) { + NSDictionary *eventAttributes = [self eventAttributesForURL:response withAction:action]; + [self removeEventDictionaryForKey:urlString]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.metricsDelegate networkConfiguration:self didGenerateMetricEvent:eventAttributes]; + }); + } + } + +} + +- (NSDictionary *)eventAttributesForURL:(NSURLResponse *)response withAction:(NSString *)action +{ + NSString *urlString = response.URL.relativePath; + NSDictionary *parameters = [self eventDictionaryForKey:urlString]; + NSDate *startDate = [parameters objectForKey:MGLStartTime]; + NSDate *endDate = [NSDate date]; + NSTimeInterval elapsedTime = [endDate timeIntervalSinceDate:startDate]; + NSDateFormatter* iso8601Formatter = [[NSDateFormatter alloc] init]; + iso8601Formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; + NSString *createdDate = [iso8601Formatter stringFromDate:[NSDate date]]; + + NSMutableArray *attributes = [NSMutableArray array]; + [attributes addObject:@{ @"name" : @"requestUrl" , @"value" : urlString }]; + [attributes addObject:@{ @"name" : MGLResourceType , @"value" : [parameters objectForKey:MGLResourceType] }]; + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSInteger responseCode = [(NSHTTPURLResponse *)response statusCode]; + [attributes addObject:@{ @"name" : @"responseCode", @"value" : @(responseCode)}]; + } + + BOOL isWIFIOn = [[MGLReachability reachabilityWithHostName:response.URL.host] isReachableViaWiFi]; + [attributes addObject:@{ @"name" : @"wifiOn", @"value" : @(isWIFIOn)}]; + + if (action) { + [attributes addObject:@{ @"name" : @"action" , @"value" : action }]; + } + + double elapsedTimeInMS = elapsedTime * 1000.0; + + return @{ + @"event" : kMGLDownloadPerformanceEvent, + @"created" : createdDate, + @"sessionId" : [NSUUID UUID].UUIDString, + @"counters" : @[ @{ @"name" : @"elapsedMS" , @"value" : @(elapsedTimeInMS) } ], + @"attributes" : attributes + }; +} + +#pragma mark - Events dictionary access + +- (nullable NSDictionary*)eventDictionaryForKey:(nonnull NSString*)key { + __block NSDictionary *dictionary; + + dispatch_sync(self.eventsQueue, ^{ + dictionary = [self.events objectForKey:key]; + }); + + return dictionary; +} + +- (void)setEventDictionary:(nonnull NSDictionary*)dictionary forKey:(nonnull NSString*)key { + dispatch_barrier_async(self.eventsQueue, ^{ + [self.events setObject:dictionary forKey:key]; + }); +} + +- (void)removeEventDictionaryForKey:(nonnull NSString*)key { + dispatch_barrier_async(self.eventsQueue, ^{ + [self.events removeObjectForKey:key]; + }); +} + +@end |