BTAnalyticsMetadata.m 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #import "BTAnalyticsMetadata.h"
  2. #import "Braintree-Version.h"
  3. #import "BTKeychain.h"
  4. #import <sys/sysctl.h>
  5. #import <sys/utsname.h>
  6. #import <UIKit/UIKit.h>
  7. @implementation BTAnalyticsMetadata
  8. + (NSDictionary *)metadata {
  9. BTAnalyticsMetadata *m = [[BTAnalyticsMetadata alloc] init];
  10. NSMutableDictionary *data = [NSMutableDictionary dictionaryWithCapacity:16];
  11. [self setObject:[m platform] forKey:@"platform" inDictionary:data];
  12. [self setObject:[m platformVersion] forKey:@"platformVersion" inDictionary:data];
  13. [self setObject:[m sdkVersion] forKey:@"sdkVersion" inDictionary:data];
  14. [self setObject:[m merchantAppId] forKey:@"merchantAppId" inDictionary:data];
  15. [self setObject:[m merchantAppName] forKey:@"merchantAppName" inDictionary:data];
  16. [self setObject:[m merchantAppVersion] forKey:@"merchantAppVersion" inDictionary:data];
  17. #ifndef __IPHONE_8_0
  18. [self setObject:@([m deviceRooted]) forKey:@"deviceRooted" inDictionary:data];
  19. #endif
  20. [self setObject:[m deviceManufacturer] forKey:@"deviceManufacturer" inDictionary:data];
  21. [self setObject:[m deviceModel] forKey:@"deviceModel" inDictionary:data];
  22. [self setObject:[m iosDeviceName] forKey:@"iosDeviceName" inDictionary:data];
  23. [self setObject:[m iosSystemName] forKey:@"iosSystemName" inDictionary:data];
  24. [self setObject:[m iosBaseSDK] forKey:@"iosBaseSDK" inDictionary:data];
  25. [self setObject:[m iosDeploymentTarget] forKey:@"iosDeploymentTarget" inDictionary:data];
  26. [self setObject:[m iosIdentifierForVendor] forKey:@"iosIdentifierForVendor" inDictionary:data];
  27. [self setObject:@([m iosIsCocoapods]) forKey:@"iosIsCocoapods" inDictionary:data];
  28. [self setObject:[m deviceAppGeneratedPersistentUuid] forKey:@"deviceAppGeneratedPersistentUuid" inDictionary:data];
  29. [self setObject:@([m isSimulator]) forKey:@"isSimulator" inDictionary:data];
  30. [self setObject:[m deviceScreenOrientation] forKey:@"deviceScreenOrientation" inDictionary:data];
  31. [self setObject:@([m isVenmoInstalled]) forKey:@"venmoInstalled" inDictionary:data];
  32. [self setObject:[m dropInVersion] forKey:@"dropinVersion" inDictionary:data];
  33. return [NSDictionary dictionaryWithDictionary:data];
  34. }
  35. + (void)setObject:(id)object forKey:(id<NSCopying>)aKey inDictionary:(NSMutableDictionary *)dictionary {
  36. if (object) {
  37. [dictionary setObject:object forKey:aKey];
  38. }
  39. }
  40. #pragma mark Metadata Factors
  41. - (NSString *)platform {
  42. return @"iOS";
  43. }
  44. - (NSString *)platformVersion {
  45. return UIDevice.currentDevice.systemVersion;
  46. }
  47. - (NSString *)sdkVersion {
  48. return BRAINTREE_VERSION;
  49. }
  50. - (NSString *)merchantAppId {
  51. return [NSBundle.mainBundle.infoDictionary objectForKey:(__bridge NSString *)kCFBundleIdentifierKey];
  52. }
  53. - (NSString *)merchantAppVersion {
  54. return [NSBundle.mainBundle.infoDictionary objectForKey:(__bridge NSString *)kCFBundleVersionKey];
  55. }
  56. - (NSString *)merchantAppName {
  57. return [NSBundle.mainBundle.infoDictionary objectForKey:(__bridge NSString *)kCFBundleNameKey];
  58. }
  59. - (BOOL)deviceRooted {
  60. #if TARGET_IPHONE_SIMULATOR || __IPHONE_8_0
  61. return NO;
  62. #else
  63. BOOL isJailbroken = system(NULL) == 1;
  64. return isJailbroken;
  65. #endif
  66. }
  67. - (NSString *)deviceManufacturer {
  68. return @"Apple";
  69. }
  70. - (NSString *)deviceModel {
  71. struct utsname systemInfo;
  72. uname(&systemInfo);
  73. NSString* code = [NSString stringWithCString:systemInfo.machine
  74. encoding:NSUTF8StringEncoding];
  75. return code;
  76. }
  77. - (NSString *)iosIdentifierForVendor {
  78. return UIDevice.currentDevice.identifierForVendor.UUIDString;
  79. }
  80. - (NSString *)iosDeploymentTarget {
  81. NSString *rawVersionString = NSBundle.mainBundle.infoDictionary[@"MinimumOSVersion"];
  82. NSArray<NSString *> *rawVersionArray = [rawVersionString componentsSeparatedByString:@"."];
  83. NSInteger formattedVersionNumber = rawVersionArray[0].integerValue * 10000;
  84. if (rawVersionArray.count > 1) {
  85. formattedVersionNumber += rawVersionArray[1].integerValue * 100;
  86. }
  87. return [NSString stringWithFormat:@"%@", @(formattedVersionNumber)];
  88. }
  89. - (NSString *)iosBaseSDK {
  90. return [@(__IPHONE_OS_VERSION_MAX_ALLOWED) stringValue];
  91. }
  92. - (NSString *)iosDeviceName {
  93. return UIDevice.currentDevice.name;
  94. }
  95. - (NSString *)iosSystemName {
  96. return UIDevice.currentDevice.systemName;
  97. }
  98. - (BOOL)iosIsCocoapods {
  99. #ifdef COCOAPODS
  100. return YES;
  101. #else
  102. return NO;
  103. #endif
  104. }
  105. - (NSString *)deviceAppGeneratedPersistentUuid {
  106. @try {
  107. static NSString *deviceAppGeneratedPersistentUuidKeychainKey = @"deviceAppGeneratedPersistentUuid";
  108. NSString *savedIdentifier = [BTKeychain stringForKey:deviceAppGeneratedPersistentUuidKeychainKey];
  109. if (savedIdentifier.length == 0) {
  110. savedIdentifier = [[NSUUID UUID] UUIDString];
  111. BOOL setDidSucceed = [BTKeychain setString:savedIdentifier
  112. forKey:deviceAppGeneratedPersistentUuidKeychainKey];
  113. if (!setDidSucceed) {
  114. return nil;
  115. }
  116. }
  117. return savedIdentifier;
  118. } @catch (NSException *exception) {
  119. return nil;
  120. }
  121. }
  122. - (BOOL)isSimulator {
  123. return TARGET_IPHONE_SIMULATOR;
  124. }
  125. - (NSString *)deviceScreenOrientation {
  126. if ([self.class isAppExtension]) {
  127. return @"AppExtension";
  128. }
  129. if ([UIDevice class] == nil) {
  130. return nil;
  131. }
  132. switch ([[UIDevice currentDevice] orientation]) {
  133. case UIDeviceOrientationFaceUp:
  134. return @"FaceUp";
  135. case UIDeviceOrientationFaceDown:
  136. return @"FaceDown";
  137. case UIDeviceOrientationPortrait:
  138. return @"Portrait";
  139. case UIDeviceOrientationPortraitUpsideDown:
  140. return @"PortraitUpsideDown";
  141. case UIDeviceOrientationLandscapeLeft:
  142. return @"LandscapeLeft";
  143. case UIDeviceOrientationLandscapeRight:
  144. return @"LandscapeRight";
  145. default:
  146. return @"Unknown";
  147. }
  148. }
  149. - (BOOL)isVenmoInstalled {
  150. if ([self.class isAppExtension]) {
  151. return NO;
  152. }
  153. UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
  154. static BOOL venmoInstalled;
  155. static dispatch_once_t onceToken;
  156. dispatch_once(&onceToken, ^{
  157. NSURL *venmoURL = [NSURL URLWithString:@"com.venmo.touch.v2://x-callback-url/vzero/auth"];
  158. venmoInstalled = [sharedApplication canOpenURL:venmoURL];
  159. });
  160. return venmoInstalled;
  161. }
  162. - (NSString *)dropInVersion {
  163. static NSString *dropInVersion;
  164. static dispatch_once_t onceToken;
  165. dispatch_once(&onceToken, ^{
  166. NSString *localizationBundlePath = [NSBundle.mainBundle pathForResource:@"Braintree-UIKit-Localization"
  167. ofType:@"bundle"];
  168. if (localizationBundlePath) {
  169. NSBundle *localizationBundle = [NSBundle bundleWithPath:localizationBundlePath];
  170. // 99.99.99 is the version specified when running the Demo app for this project.
  171. // We want to ignore it in this case and not return a version.
  172. if (localizationBundle && ! [localizationBundle.infoDictionary[@"CFBundleShortVersionString"] isEqualToString:@"99.99.99"]) {
  173. dropInVersion = localizationBundle.infoDictionary[@"CFBundleShortVersionString"];
  174. }
  175. }
  176. });
  177. return dropInVersion;
  178. }
  179. + (BOOL)isAppExtension {
  180. NSDictionary *extensionDictionary = NSBundle.mainBundle.infoDictionary[@"NSExtension"];
  181. return [extensionDictionary isKindOfClass:[NSDictionary class]];
  182. }
  183. @end