diff --git a/Example/GrowingAnalyticsTests/TrackerCoreTests/EventTests/EventTest.m b/Example/GrowingAnalyticsTests/TrackerCoreTests/EventTests/EventTest.m index 248475908..2e534eb39 100644 --- a/Example/GrowingAnalyticsTests/TrackerCoreTests/EventTests/EventTest.m +++ b/Example/GrowingAnalyticsTests/TrackerCoreTests/EventTests/EventTest.m @@ -212,51 +212,33 @@ - (void)testGrowingPageEvent { .setAttributes(@{@"key" : @"value"}); [GrowingEventManager.sharedInstance postEventBuilder:builder]; - // !!! 注意:这里有个隐藏的死锁问题 !!! - // 首次发送 GrowingPageEvent 时,-[GrowingDeviceInfo deviceOrientation] 中,有个子线程同步等待主线程的操作 - // 如果此时主线程也在同步等待子线程,则会造成死锁,比如在主线程调用以下代码: - // [GrowingDispatchManager dispatchInGrowingThread:^{} waitUntilDone:YES]; - // 因此,这里在子线程验证PageEvent - XCTestExpectation *expectation = [self expectationWithDescription:@"testGrowingPageEvent Test failed : timeout"]; - dispatch_async(dispatch_get_global_queue(0, 0), ^{ - NSArray *events = [MockEventQueue.sharedQueue eventsFor:GrowingEventTypePage]; - XCTAssertEqual(events.count, 1); - - GrowingPageEvent *event = (GrowingPageEvent *)events.firstObject; - XCTAssertEqualObjects(event.eventType, GrowingEventTypePage); - XCTAssertEqualObjects(event.pageName, @"path"); - XCTAssertEqualObjects(event.orientation, orientation); - XCTAssertEqualObjects(event.title, @"title"); - XCTAssertEqualObjects(event.referralPage, @"referralPage"); - XCTAssertEqualObjects(event.attributes[@"key"], @"value"); - - NSDictionary *dic = event.toDictionary; - XCTAssertEqualObjects(dic[@"eventType"], GrowingEventTypePage); - XCTAssertEqualObjects(dic[@"path"], @"path"); - XCTAssertEqualObjects(dic[@"orientation"], orientation); - XCTAssertEqualObjects(dic[@"title"], @"title"); - XCTAssertEqualObjects(dic[@"referralPage"], @"referralPage"); - XCTAssertEqualObjects(dic[@"attributes"][@"key"], @"value"); - - NSMutableDictionary *mDic = dic.mutableCopy; - if (dic[@"orientation"] == nil && orientation == nil) { - // 在无HostApplication的Logic Test时,orientation将为nil,这里手动赋值为PORTRAIT - mDic[@"orientation"] = @"PORTRAIT"; - } - XCTAssertTrue([ManualTrackHelper pageEventCheck:mDic]); - XCTAssertTrue([ManualTrackHelper contextOptionalPropertyCheck:mDic]); - - [expectation fulfill]; - }); + NSArray *events = [MockEventQueue.sharedQueue eventsFor:GrowingEventTypePage]; + XCTAssertEqual(events.count, 1); - [self waitForExpectationsWithTimeout:3.0f handler:nil]; + GrowingPageEvent *event = (GrowingPageEvent *)events.firstObject; + XCTAssertEqualObjects(event.eventType, GrowingEventTypePage); + XCTAssertEqualObjects(event.pageName, @"path"); + XCTAssertEqualObjects(event.orientation, orientation); + XCTAssertEqualObjects(event.title, @"title"); + XCTAssertEqualObjects(event.referralPage, @"referralPage"); + XCTAssertEqualObjects(event.attributes[@"key"], @"value"); + + NSDictionary *dic = event.toDictionary; + XCTAssertEqualObjects(dic[@"eventType"], GrowingEventTypePage); + XCTAssertEqualObjects(dic[@"path"], @"path"); + XCTAssertEqualObjects(dic[@"orientation"], orientation); + XCTAssertEqualObjects(dic[@"title"], @"title"); + XCTAssertEqualObjects(dic[@"referralPage"], @"referralPage"); + XCTAssertEqualObjects(dic[@"attributes"][@"key"], @"value"); + XCTAssertTrue([ManualTrackHelper pageEventCheck:dic]); + XCTAssertTrue([ManualTrackHelper contextOptionalPropertyCheck:dic]); } #pragma mark - Private Methods - (NSString *)deviceOrientation { // SDK配置pageEvent.orientation的逻辑 - __block NSString *deviceOrientation = nil; + __block NSString *deviceOrientation = @"PORTRAIT"; dispatch_block_t block = ^{ UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; if (orientation != UIInterfaceOrientationUnknown) { diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 43497b16e..492636e71 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -161,7 +161,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - GrowingAnalytics: 0f45c3be51d1daaeabc98025586c448922c8ae72 + GrowingAnalytics: 36c504c30f5ab374148cbcf7eb0e32cb85ceaeea GrowingAnalytics-cdp: 08c85179967c06c36e7377731e2fc7aefc8c470f GrowingAPM: 3c4de0384935b654e6798b95606f47883a99418b GrowingToolsKit: 53160d19690da0b78e04a9242abde7af86442922 @@ -174,4 +174,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8b74d64200d8437dbd5c21a406bd3513fc0eebd0 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.1 diff --git a/GrowingAnalytics-cdp.podspec b/GrowingAnalytics-cdp.podspec index d047cbf19..fac7c8497 100644 --- a/GrowingAnalytics-cdp.podspec +++ b/GrowingAnalytics-cdp.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GrowingAnalytics-cdp' - s.version = '3.8.5' + s.version = '3.8.6' s.summary = 'iOS SDK of GrowingIO.' s.description = <<-DESC GrowingAnalytics-cdp基于GrowingAnalytics,同样具备自动采集基本的用户行为事件,比如访问和行为数据等。 diff --git a/GrowingAnalytics.podspec b/GrowingAnalytics.podspec index a42da5a03..99071a2f7 100644 --- a/GrowingAnalytics.podspec +++ b/GrowingAnalytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GrowingAnalytics' - s.version = '3.8.5' + s.version = '3.8.6' s.summary = 'iOS SDK of GrowingIO.' s.description = <<-DESC GrowingAnalytics具备自动采集基本的用户行为事件,比如访问和行为数据等。目前支持代码埋点、无埋点、可视化圈选、热图等功能。 diff --git a/GrowingTrackerCore/GrowingRealTracker.m b/GrowingTrackerCore/GrowingRealTracker.m index 00e009b6d..00c9e173c 100644 --- a/GrowingTrackerCore/GrowingRealTracker.m +++ b/GrowingTrackerCore/GrowingRealTracker.m @@ -38,8 +38,8 @@ #import "GrowingTrackerCore/Utils/GrowingDeviceInfo.h" #import "GrowingULAppLifecycle.h" -NSString *const GrowingTrackerVersionName = @"3.8.5"; -const int GrowingTrackerVersionCode = 30805; +NSString *const GrowingTrackerVersionName = @"3.8.6"; +const int GrowingTrackerVersionCode = 30806; @interface GrowingRealTracker () @@ -57,11 +57,9 @@ - (instancetype)initWithConfiguration:(GrowingTrackConfiguration *)configuration _configuration = [configuration copyWithZone:nil]; _launchOptions = [launchOptions copy]; GrowingConfigurationManager.sharedInstance.trackConfiguration = self.configuration; - if (configuration.urlScheme.length > 0) { - [GrowingDeviceInfo configUrlScheme:configuration.urlScheme.copy]; - } [self loggerSetting]; + [GrowingDeviceInfo setup]; [GrowingULAppLifecycle setup]; [GrowingSession startSession]; #if TARGET_OS_IOS diff --git a/GrowingTrackerCore/Utils/GrowingDeviceInfo.h b/GrowingTrackerCore/Utils/GrowingDeviceInfo.h index 4d1f6ebd5..226cdbb16 100644 --- a/GrowingTrackerCore/Utils/GrowingDeviceInfo.h +++ b/GrowingTrackerCore/Utils/GrowingDeviceInfo.h @@ -40,8 +40,9 @@ @property (nonatomic, readonly, assign) CGFloat screenWidth; @property (nonatomic, readonly, assign) CGFloat screenHeight; @property (nonatomic, copy) NSString * (^encryptStringBlock)(NSString *string); +@property (nonatomic, readonly, assign) NSInteger timezoneOffset; + (instancetype)currentDeviceInfo; -+ (void)configUrlScheme:(NSString *)urlScheme; ++ (void)setup; @end diff --git a/GrowingTrackerCore/Utils/GrowingDeviceInfo.m b/GrowingTrackerCore/Utils/GrowingDeviceInfo.m index 30dbd3ee0..c0383138c 100644 --- a/GrowingTrackerCore/Utils/GrowingDeviceInfo.m +++ b/GrowingTrackerCore/Utils/GrowingDeviceInfo.m @@ -18,30 +18,22 @@ // limitations under the License. #import "GrowingTrackerCore/Utils/GrowingDeviceInfo.h" +#import "GrowingTargetConditionals.h" -#if __has_include() -#import -#endif - -#if __has_include() -#import -#endif - -#import -#import #import -#include +#import #import #import "GrowingTrackerCore/Helpers/GrowingHelpers.h" +#import "GrowingTrackerCore/Manager/GrowingConfigurationManager.h" #import "GrowingTrackerCore/Thirdparty/Logger/GrowingLogger.h" #import "GrowingTrackerCore/Thread/GrowingDispatchManager.h" #import "GrowingTrackerCore/Utils/GrowingInternalMacros.h" #import "GrowingTrackerCore/Utils/GrowingKeyChainWrapper.h" #import "GrowingTrackerCore/Utils/UserIdentifier/GrowingUserIdentifier.h" #import "GrowingULAppLifecycle.h" +#import "GrowingULApplication.h" -static NSString *kGrowingUrlScheme = nil; NSString *const kGrowingKeychainUserIdKey = @"kGrowingIOKeychainUserIdKey"; @interface GrowingDeviceInfo () @@ -65,6 +57,7 @@ @interface GrowingDeviceInfo () @property (nonatomic, readwrite, assign) int appState; @property (nonatomic, readwrite, assign) CGFloat screenWidth; @property (nonatomic, readwrite, assign) CGFloat screenHeight; +@property (nonatomic, readwrite, assign) NSInteger timezoneOffset; @end @@ -80,10 +73,35 @@ - (instancetype)init { _infoDictionary = [[NSBundle mainBundle] infoDictionary]; _deviceBrand = @"Apple"; _appState = 0; + _timezoneOffset = -([[NSTimeZone defaultTimeZone] secondsFromGMT] / 60); - [[GrowingULAppLifecycle sharedInstance] addAppLifecycleDelegate:self]; +#if Growing_USE_APPKIT + _screenWidth = NSScreen.mainScreen.frame.size.width; + _screenHeight = NSScreen.mainScreen.frame.size.height; +#elif Growing_OS_IOS || Growing_OS_MACCATALYST || Growing_OS_TV + UIScreen *screen = [UIScreen mainScreen]; + CGFloat width = screen.bounds.size.width * screen.scale; + CGFloat height = screen.bounds.size.height * screen.scale; + // make sure the size is in portrait to keep consistency + _screenWidth = MIN(width, height); + _screenHeight = MAX(width, height); +#elif Growing_USE_WATCHKIT + _screenWidth = WKInterfaceDevice.currentDevice.screenBounds.size.width; + _screenHeight = WKInterfaceDevice.currentDevice.screenBounds.size.height; +#else + _screenWidth = 1; + _screenHeight = 1; +#endif + NSString *urlScheme = GrowingConfigurationManager.sharedInstance.trackConfiguration.urlScheme; + _urlScheme = urlScheme.length > 0 ? urlScheme.copy : [self getCurrentUrlScheme]; + + _deviceOrientation = @"PORTRAIT"; +#if Growing_OS_PURE_IOS + UIInterfaceOrientation orientation = [[GrowingULApplication sharedApplication] statusBarOrientation]; + if (orientation != UIInterfaceOrientationUnknown) { + _deviceOrientation = UIInterfaceOrientationIsPortrait(orientation) ? @"PORTRAIT" : @"LANDSCAPE"; + } -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleStatusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification @@ -95,6 +113,12 @@ - (instancetype)init { #pragma mark - Public Methods ++ (void)setup { + // 初始化urlScheme、appState、deviceOrientation等等(需要保证在主线程执行) + GrowingDeviceInfo *deviceInfo = [GrowingDeviceInfo currentDeviceInfo]; + [[GrowingULAppLifecycle sharedInstance] addAppLifecycleDelegate:deviceInfo]; +} + + (instancetype)currentDeviceInfo { static GrowingDeviceInfo *info = nil; static dispatch_once_t onceToken; @@ -104,16 +128,13 @@ + (instancetype)currentDeviceInfo { return info; } -+ (void)configUrlScheme:(NSString *)urlScheme { - kGrowingUrlScheme = urlScheme; -} - #pragma mark - Private Methods - (NSString *)getCurrentUrlScheme { - for (NSDictionary *dic in _infoDictionary[@"CFBundleURLTypes"]) { - NSArray *shemes = dic[@"CFBundleURLSchemes"]; - for (NSString *urlScheme in shemes) { + NSArray *urlTypes = _infoDictionary[@"CFBundleURLTypes"]; + for (NSDictionary *dic in urlTypes) { + NSArray *schemes = dic[@"CFBundleURLSchemes"]; + for (NSString *urlScheme in schemes) { if ([urlScheme isKindOfClass:[NSString class]] && [urlScheme hasPrefix:@"growing."]) { return urlScheme; } @@ -123,7 +144,7 @@ - (NSString *)getCurrentUrlScheme { } - (NSString *)getDeviceIdString { -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#if Growing_OS_PURE_IOS || Growing_OS_WATCH || Growing_OS_VISION || Growing_OS_TV NSString *deviceIdString = [GrowingKeyChainWrapper keyChainObjectForKey:kGrowingKeychainUserIdKey]; if ([deviceIdString growingHelper_isValidU]) { return deviceIdString; @@ -131,28 +152,28 @@ - (NSString *)getDeviceIdString { #endif NSString *uuid = [GrowingUserIdentifier getUserIdentifier]; -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#if Growing_OS_PURE_IOS || Growing_OS_WATCH || Growing_OS_VISION || Growing_OS_TV [GrowingKeyChainWrapper setKeychainObject:uuid forKey:kGrowingKeychainUserIdKey]; #endif return uuid; } -+ (NSString *)getSysInfoByName:(char *)typeSpeifier { ++ (NSString *)getSysInfoByName:(char *)name { size_t size; - sysctlbyname(typeSpeifier, NULL, &size, NULL, 0); - char *answer = (char *)malloc(size); - sysctlbyname(typeSpeifier, answer, &size, NULL, 0); - NSString *results = [NSString stringWithCString:answer encoding:NSUTF8StringEncoding]; - if (results == nil) { - results = @""; + NSString *results = nil; + if (sysctlbyname(name, NULL, &size, NULL, 0) == 0) { + char *machine = calloc(1, size); + if (sysctlbyname(name, machine, &size, NULL, 0) == 0) { + results = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding]; + } + free(machine); } - free(answer); - return results; + return results ?: @""; } -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#if Growing_OS_PURE_IOS - (void)handleStatusBarOrientationChange:(NSNotification *)notification { - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + UIInterfaceOrientation orientation = [[GrowingULApplication sharedApplication] statusBarOrientation]; if (orientation != UIInterfaceOrientationUnknown) { _deviceOrientation = UIInterfaceOrientationIsPortrait(orientation) ? @"PORTRAIT" : @"LANDSCAPE"; } @@ -161,14 +182,17 @@ - (void)handleStatusBarOrientationChange:(NSNotification *)notification { - (void)updateAppState { dispatch_block_t block = ^{ -#if TARGET_OS_OSX - self->_appState = [NSApplication sharedApplication].isActive ? 0 : 1; -#else - self->_appState = [UIApplication sharedApplication].applicationState == UIApplicationStateActive ? 0 : 1; +#if Growing_USE_APPKIT + self->_appState = [[GrowingULApplication sharedApplication] isActive] ? 0 : 1; +#elif Growing_USE_UIKIT + self->_appState = + [[GrowingULApplication sharedApplication] applicationState] == UIApplicationStateActive ? 0 : 1; +#elif Growing_USE_WATCHKIT + self->_appState = [WKApplication sharedApplication].applicationState == WKApplicationStateActive ? 0 : 1; #endif }; -#if !TARGET_OS_OSX +#if !Growing_OS_OSX if (@available(iOS 13.0, *)) { // iOS 13当收到UISceneWillDeactivateNotification/UISceneDidActivateNotification时,applicationState并未转换 NSDictionary *sceneManifestDict = _infoDictionary[@"UIApplicationSceneManifest"]; @@ -234,12 +258,16 @@ - (NSString *)language { - (NSString *)deviceModel { if (!_deviceModel) { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST +#if Growing_USE_APPKIT || Growing_OS_MACCATALYST _deviceModel = [GrowingDeviceInfo getSysInfoByName:(char *)"hw.model"]; -#elif TARGET_OS_IOS +#elif Growing_USE_UIKIT struct utsname systemInfo; uname(&systemInfo); _deviceModel = @(systemInfo.machine); +#elif Growing_USE_WATCHKIT + _deviceModel = [GrowingDeviceInfo getSysInfoByName:(char *)"hw.machine"]; +#else + _deviceModel = @"Undefined"; #endif } return _deviceModel; @@ -247,10 +275,14 @@ - (NSString *)deviceModel { - (NSString *)deviceType { if (!_deviceType) { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST +#if Growing_USE_APPKIT || Growing_OS_MACCATALYST _deviceType = @"Mac"; -#elif TARGET_OS_IOS +#elif Growing_USE_UIKIT _deviceType = [UIDevice currentDevice].model; +#elif Growing_USE_WATCHKIT + _deviceType = [WKInterfaceDevice currentDevice].model; +#else + _deviceType = @"Undefined"; #endif } return _deviceType; @@ -258,12 +290,20 @@ - (NSString *)deviceType { - (NSString *)platform { if (!_platform) { -#if TARGET_OS_OSX +#if Growing_OS_OSX _platform = @"macOS"; -#elif TARGET_OS_MACCATALYST +#elif Growing_OS_MACCATALYST _platform = @"MacCatalyst"; -#elif TARGET_OS_IOS +#elif Growing_OS_PURE_IOS _platform = @"iOS"; +#elif Growing_OS_WATCH + _platform = @"watchOS"; +#elif Growing_OS_TV + _platform = @"tvOS"; +#elif Growing_OS_VISION + _platform = @"visionOS"; +#else + _platform = @"Undefined"; #endif } return _platform; @@ -271,12 +311,16 @@ - (NSString *)platform { - (NSString *)platformVersion { if (!_platformVersion) { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST +#if Growing_USE_APPKIT || Growing_OS_MACCATALYST NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; _platformVersion = dic[@"ProductVersion"]; -#elif TARGET_OS_IOS +#elif Growing_USE_UIKIT _platformVersion = [UIDevice currentDevice].systemVersion; +#elif Growing_USE_WATCHKIT + _platformVersion = [WKInterfaceDevice currentDevice].systemVersion; +#else + _platformVersion = @"1.0"; #endif } return _platformVersion; @@ -296,36 +340,6 @@ - (NSString *)appVersion { return _appVersion; } -- (NSString *)urlScheme { - if (!_urlScheme) { - _urlScheme = kGrowingUrlScheme ?: [self getCurrentUrlScheme]; - } - return _urlScheme; -} - -- (NSString *)deviceOrientation { - if (!_deviceOrientation) { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST - _deviceOrientation = @"PORTRAIT"; -#elif TARGET_OS_IOS - dispatch_block_t block = ^{ - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; - if (orientation != UIInterfaceOrientationUnknown) { - self->_deviceOrientation = UIInterfaceOrientationIsPortrait(orientation) ? @"PORTRAIT" : @"LANDSCAPE"; - } - }; - if ([NSThread isMainThread]) { - block(); - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ - block(); - }); - } -#endif - } - return _deviceOrientation; -} - - (NSString *)idfv { if (!_idfv) { _idfv = [GrowingUserIdentifier idfv]; @@ -340,34 +354,4 @@ - (NSString *)idfa { return _idfa; } -- (CGFloat)screenWidth { - if (!_screenWidth) { -#if !TARGET_OS_OSX - UIScreen *screen = [UIScreen mainScreen]; - CGFloat width = screen.bounds.size.width * screen.scale; - CGFloat height = screen.bounds.size.height * screen.scale; - // make sure the size is in portrait to keep consistency - _screenWidth = MIN(width, height); -#else - _screenWidth = NSScreen.mainScreen.frame.size.width; -#endif - } - return _screenWidth; -} - -- (CGFloat)screenHeight { - if (!_screenHeight) { -#if !TARGET_OS_OSX - UIScreen *screen = [UIScreen mainScreen]; - CGFloat width = screen.bounds.size.width * screen.scale; - CGFloat height = screen.bounds.size.height * screen.scale; - // make sure the size is in portrait to keep consistency - _screenHeight = MAX(width, height); -#else - _screenHeight = NSScreen.mainScreen.frame.size.height; -#endif - } - return _screenHeight; -} - @end