BTAnalyticsMetadata.m 7.3 KB

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