diff --git a/SensorsAnalyticsSDK.podspec b/SensorsAnalyticsSDK.podspec index e8217e76..c3bc147a 100644 --- a/SensorsAnalyticsSDK.podspec +++ b/SensorsAnalyticsSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SensorsAnalyticsSDK" - s.version = "4.2.6" + s.version = "4.3.0" s.summary = "The official iOS SDK of Sensors Analytics." s.homepage = "http://www.sensorsdata.cn" s.source = { :git => 'https://github.com/sensorsdata/sa-sdk-ios.git', :tag => "v#{s.version}" } @@ -45,14 +45,6 @@ Pod::Spec.new do |s| c.osx.dependency 'SensorsAnalyticsSDK/Common' end - # 支持 CAID 渠道匹配 - s.subspec 'CAID' do |f| - f.ios.deployment_target = '8.0' - f.dependency 'SensorsAnalyticsSDK/Core' - f.source_files = "SensorsAnalyticsSDK/CAID/**/*.{h,m}" - f.private_header_files = 'SensorsAnalyticsSDK/CAID/**/*.h' - end - # 全埋点 s.subspec 'AutoTrack' do |g| g.ios.deployment_target = '8.0' diff --git a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj index 8b2bde6b..f3878cf1 100644 --- a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj +++ b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj @@ -294,8 +294,8 @@ A8BCC4A82686C2D800B72040 /* SAConfigOptions+Encrypt.m in Sources */ = {isa = PBXBuildFile; fileRef = A8BCC4A72686C2D800B72040 /* SAConfigOptions+Encrypt.m */; }; A8BCC4AB2686C42D00B72040 /* SASecretKey.h in Headers */ = {isa = PBXBuildFile; fileRef = A8BCC4A92686C42D00B72040 /* SASecretKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; A8BCC4AC2686C42D00B72040 /* SASecretKey.m in Sources */ = {isa = PBXBuildFile; fileRef = A8BCC4AA2686C42D00B72040 /* SASecretKey.m */; }; - A8BCC4D926872A3F00B72040 /* SADeeplinkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A8BCC4D726872A3F00B72040 /* SADeeplinkManager.h */; }; - A8BCC4DA26872A3F00B72040 /* SADeeplinkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A8BCC4D826872A3F00B72040 /* SADeeplinkManager.m */; }; + A8BCC4D926872A3F00B72040 /* SADeepLinkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A8BCC4D726872A3F00B72040 /* SADeepLinkManager.h */; }; + A8BCC4DA26872A3F00B72040 /* SADeepLinkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A8BCC4D826872A3F00B72040 /* SADeepLinkManager.m */; }; A8BCC4DE26872B6600B72040 /* SADebugModeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A8BCC4DC26872B6600B72040 /* SADebugModeManager.h */; }; A8BCC4DF26872B6600B72040 /* SADebugModeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A8BCC4DD26872B6600B72040 /* SADebugModeManager.m */; }; A8CC22322685E50C00E96A03 /* SARemoteConfigOperator.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CC22242685E50C00E96A03 /* SARemoteConfigOperator.h */; }; @@ -359,13 +359,28 @@ F2E4AB9E26EC86C800BA7F01 /* SensorsAnalyticsSDK+Location.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E4AB9C26EC86C800BA7F01 /* SensorsAnalyticsSDK+Location.m */; }; F2E4ABA126ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E4AB9F26ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.h */; settings = {ATTRIBUTES = (Public, ); }; }; F2E4ABA226ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E4ABA026ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.m */; }; - F2E4ABA526ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F2E4ABA626ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.m */; }; + F2E4ABA526ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F2E4ABA626ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.m */; }; F2E4ABA926ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E4ABA726ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.h */; settings = {ATTRIBUTES = (Public, ); }; }; F2E4ABAA26ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E4ABA826ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.m */; }; F2E9723125E637820009A2B9 /* SAAppPushManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E9722F25E637820009A2B9 /* SAAppPushManager.h */; }; F2E9723225E637820009A2B9 /* SAAppPushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E9723025E637820009A2B9 /* SAAppPushManager.m */; }; FC002920262C189E00A18FE3 /* SAConfigOptions+Encrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0028EF2629756400A18FE3 /* SAConfigOptions+Encrypt.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC0A8C63276334F200109267 /* SADeepLinkConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0A8C61276334F200109267 /* SADeepLinkConstants.h */; }; + FC0A8C64276334F200109267 /* SADeepLinkConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = FC0A8C62276334F200109267 /* SADeepLinkConstants.m */; }; + FC23719523D0519A00DDD708 /* SALinkHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC23719423D0519A00DDD708 /* SALinkHandlerTests.m */; }; + FC332DE727672606009122FC /* SADeepLinkProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = FC332DE527672606009122FC /* SADeepLinkProcessor.h */; }; + FC332DE827672606009122FC /* SADeepLinkProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FC332DE627672606009122FC /* SADeepLinkProcessor.m */; }; + FCBECDF327DEDF4200361D6C /* SAQueryDeepLinkProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = FCBECDF127DEDF4200361D6C /* SAQueryDeepLinkProcessor.h */; }; + FCBECDF427DEDF4200361D6C /* SAQueryDeepLinkProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FCBECDF227DEDF4200361D6C /* SAQueryDeepLinkProcessor.m */; }; + FCBECDF727DEDF6400361D6C /* SADeepLinkEventProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = FCBECDF527DEDF6400361D6C /* SADeepLinkEventProcessor.h */; }; + FCBECDF827DEDF6400361D6C /* SADeepLinkEventProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FCBECDF627DEDF6400361D6C /* SADeepLinkEventProcessor.m */; }; + FCBECDFB27DEDF7200361D6C /* SARequestDeepLinkProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = FCBECDF927DEDF7200361D6C /* SARequestDeepLinkProcessor.h */; }; + FCBECDFC27DEDF7200361D6C /* SARequestDeepLinkProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FCBECDFA27DEDF7200361D6C /* SARequestDeepLinkProcessor.m */; }; + FCBECDFF27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = FCBECDFD27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.h */; }; + FCBECE0027DEDF7F00361D6C /* SADeferredDeepLinkProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FCBECDFE27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.m */; }; + FCC02F2A26CE4EF700DB8F54 /* SAUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = FCC02F2826CE4EF700DB8F54 /* SAUserAgent.h */; }; + FCC02F2B26CE4EF700DB8F54 /* SAUserAgent.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC02F2926CE4EF700DB8F54 /* SAUserAgent.m */; }; FCE7F19D263012660018EB14 /* SAEncryptTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FCE7F19C263012660018EB14 /* SAEncryptTests.m */; }; /* End PBXBuildFile section */ @@ -688,8 +703,8 @@ A8BCC4A72686C2D800B72040 /* SAConfigOptions+Encrypt.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SAConfigOptions+Encrypt.m"; sourceTree = ""; }; A8BCC4A92686C42D00B72040 /* SASecretKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SASecretKey.h; sourceTree = ""; }; A8BCC4AA2686C42D00B72040 /* SASecretKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SASecretKey.m; sourceTree = ""; }; - A8BCC4D726872A3F00B72040 /* SADeeplinkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SADeeplinkManager.h; sourceTree = ""; }; - A8BCC4D826872A3F00B72040 /* SADeeplinkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SADeeplinkManager.m; sourceTree = ""; }; + A8BCC4D726872A3F00B72040 /* SADeepLinkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SADeepLinkManager.h; sourceTree = ""; }; + A8BCC4D826872A3F00B72040 /* SADeepLinkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SADeepLinkManager.m; sourceTree = ""; }; A8BCC4DC26872B6600B72040 /* SADebugModeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SADebugModeManager.h; sourceTree = ""; }; A8BCC4DD26872B6600B72040 /* SADebugModeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SADebugModeManager.m; sourceTree = ""; }; A8CC22242685E50C00E96A03 /* SARemoteConfigOperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SARemoteConfigOperator.h; sourceTree = ""; }; @@ -758,14 +773,29 @@ F2E4AB9C26EC86C800BA7F01 /* SensorsAnalyticsSDK+Location.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SensorsAnalyticsSDK+Location.m"; sourceTree = ""; }; F2E4AB9F26ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SensorsAnalyticsSDK+DeviceOrientation.h"; sourceTree = ""; }; F2E4ABA026ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SensorsAnalyticsSDK+DeviceOrientation.m"; sourceTree = ""; }; - F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SensorsAnalyticsSDK+Deeplink.h"; sourceTree = ""; }; - F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SensorsAnalyticsSDK+Deeplink.m"; sourceTree = ""; }; + F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SensorsAnalyticsSDK+DeepLink.h"; sourceTree = ""; }; + F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SensorsAnalyticsSDK+DeepLink.m"; sourceTree = ""; }; F2E4ABA726ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SensorsAnalyticsSDK+DebugMode.h"; sourceTree = ""; }; F2E4ABA826ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SensorsAnalyticsSDK+DebugMode.m"; sourceTree = ""; }; F2E9722F25E637820009A2B9 /* SAAppPushManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SAAppPushManager.h; sourceTree = ""; }; F2E9723025E637820009A2B9 /* SAAppPushManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SAAppPushManager.m; sourceTree = ""; }; FC0028EF2629756400A18FE3 /* SAConfigOptions+Encrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SAConfigOptions+Encrypt.h"; sourceTree = ""; }; + FC0A8C61276334F200109267 /* SADeepLinkConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SADeepLinkConstants.h; sourceTree = ""; }; + FC0A8C62276334F200109267 /* SADeepLinkConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SADeepLinkConstants.m; sourceTree = ""; }; FC23719423D0519A00DDD708 /* SALinkHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SALinkHandlerTests.m; sourceTree = ""; }; + FC332DE527672606009122FC /* SADeepLinkProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SADeepLinkProcessor.h; sourceTree = ""; }; + FC332DE627672606009122FC /* SADeepLinkProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SADeepLinkProcessor.m; sourceTree = ""; }; + FCBECDF127DEDF4200361D6C /* SAQueryDeepLinkProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SAQueryDeepLinkProcessor.h; sourceTree = ""; }; + FCBECDF227DEDF4200361D6C /* SAQueryDeepLinkProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SAQueryDeepLinkProcessor.m; sourceTree = ""; }; + FCBECDF527DEDF6400361D6C /* SADeepLinkEventProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SADeepLinkEventProcessor.h; sourceTree = ""; }; + FCBECDF627DEDF6400361D6C /* SADeepLinkEventProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SADeepLinkEventProcessor.m; sourceTree = ""; }; + FCBECDF927DEDF7200361D6C /* SARequestDeepLinkProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SARequestDeepLinkProcessor.h; sourceTree = ""; }; + FCBECDFA27DEDF7200361D6C /* SARequestDeepLinkProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SARequestDeepLinkProcessor.m; sourceTree = ""; }; + FCBECDFD27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SADeferredDeepLinkProcessor.h; sourceTree = ""; }; + FCBECDFE27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SADeferredDeepLinkProcessor.m; sourceTree = ""; }; + FCC02F1B26CB9FB900DB8F54 /* SafariServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/iOSSupport/System/Library/Frameworks/SafariServices.framework; sourceTree = DEVELOPER_DIR; }; + FCC02F2826CE4EF700DB8F54 /* SAUserAgent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SAUserAgent.h; sourceTree = ""; }; + FCC02F2926CE4EF700DB8F54 /* SAUserAgent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SAUserAgent.m; sourceTree = ""; }; FCE7F19C263012660018EB14 /* SAEncryptTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SAEncryptTests.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -948,6 +978,7 @@ 4D8CE4C522E872B400829B29 /* Frameworks */ = { isa = PBXGroup; children = ( + FCC02F1B26CB9FB900DB8F54 /* SafariServices.framework */, 4D8CE4C622E872B400829B29 /* Security.framework */, ); name = Frameworks; @@ -1437,10 +1468,24 @@ A8BCC4D626872A3F00B72040 /* Deeplink */ = { isa = PBXGroup; children = ( - A8BCC4D726872A3F00B72040 /* SADeeplinkManager.h */, - A8BCC4D826872A3F00B72040 /* SADeeplinkManager.m */, - F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.h */, - F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.m */, + FCC02F2826CE4EF700DB8F54 /* SAUserAgent.h */, + FCC02F2926CE4EF700DB8F54 /* SAUserAgent.m */, + FC0A8C61276334F200109267 /* SADeepLinkConstants.h */, + FC0A8C62276334F200109267 /* SADeepLinkConstants.m */, + FC332DE527672606009122FC /* SADeepLinkProcessor.h */, + FC332DE627672606009122FC /* SADeepLinkProcessor.m */, + FCBECDF527DEDF6400361D6C /* SADeepLinkEventProcessor.h */, + FCBECDF627DEDF6400361D6C /* SADeepLinkEventProcessor.m */, + FCBECDF127DEDF4200361D6C /* SAQueryDeepLinkProcessor.h */, + FCBECDF227DEDF4200361D6C /* SAQueryDeepLinkProcessor.m */, + FCBECDF927DEDF7200361D6C /* SARequestDeepLinkProcessor.h */, + FCBECDFA27DEDF7200361D6C /* SARequestDeepLinkProcessor.m */, + FCBECDFD27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.h */, + FCBECDFE27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.m */, + A8BCC4D726872A3F00B72040 /* SADeepLinkManager.h */, + A8BCC4D826872A3F00B72040 /* SADeepLinkManager.m */, + F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.h */, + F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.m */, ); path = Deeplink; sourceTree = ""; @@ -1653,7 +1698,7 @@ buildActionMask = 2147483647; files = ( 4DD1285525F872A4008C0B1E /* SAObjectIdentityProvider.h in Headers */, - F2E4ABA526ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.h in Headers */, + F2E4ABA526ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.h in Headers */, A8356DD62656459A00FD64AA /* UIGestureRecognizer+SAAutoTrack.h in Headers */, 4DD1285F25F872A4008C0B1E /* SAVisualizedSnapshotMessage.h in Headers */, 4D4DB2C825B7D19C00938842 /* SADelegateProxy.h in Headers */, @@ -1668,10 +1713,12 @@ A806642B26905F6C00FFDEBA /* SAChannelMatchManager.h in Headers */, A8BCC4DE26872B6600B72040 /* SADebugModeManager.h in Headers */, 45A565BC263C17E400C9C41B /* SAReferrerManager.h in Headers */, + FCBECDFF27DEDF7F00361D6C /* SADeferredDeepLinkProcessor.h in Headers */, 4D57261E26206D5300B2AC9F /* SAVisualizedLogger.h in Headers */, 881A420F253D7B5000854F69 /* SAAlertController.h in Headers */, F277F5C025CF9A43009B5CE6 /* SAUNUserNotificationCenterDelegateProxy.h in Headers */, A8356DC32656459A00FD64AA /* SAAutoTrackProperty.h in Headers */, + FC332DE727672606009122FC /* SADeepLinkProcessor.h in Headers */, 4D2D53C22591EB3A00805141 /* SAReadWriteLock.h in Headers */, F2E4ABA126ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.h in Headers */, A8356DBD2656459A00FD64AA /* SAAutoTrackUtils.h in Headers */, @@ -1691,6 +1738,7 @@ 45A565BF263C17E400C9C41B /* SAAppLifecycle.h in Headers */, F277F5C525CF9A43009B5CE6 /* SAAppPushConstants.h in Headers */, F23CA0052701715E002EEACA /* WKWebView+SABridge.h in Headers */, + FCBECDFB27DEDF7200361D6C /* SARequestDeepLinkProcessor.h in Headers */, A8CC22392685E50C00E96A03 /* SARemoteConfigEventObject.h in Headers */, 4DA89BC325C2BC1E003ABA43 /* SANetwork.h in Headers */, 881A41A2253D7B4F00854F69 /* SAModuleManager.h in Headers */, @@ -1711,6 +1759,7 @@ 886E1E212726AC420084D1B3 /* SADeviceIDPropertyPlugin.h in Headers */, 4DD1285125F872A4008C0B1E /* SAApplicationStateSerializer.h in Headers */, F23CA0082701715E002EEACA /* SensorsAnalyticsSDK+JavaScriptBridge.h in Headers */, + FCBECDF327DEDF4200361D6C /* SAQueryDeepLinkProcessor.h in Headers */, A8356DC12656459A00FD64AA /* SAAppEndTracker.h in Headers */, A82E8957267D918100475757 /* SASecretKeyFactory.h in Headers */, 4DD1284B25F872A4008C0B1E /* SAWebElementView.h in Headers */, @@ -1732,6 +1781,7 @@ 881A4214253D7B5000854F69 /* SAKeyChainItemWrapper.h in Headers */, A8CC223C2685E50C00E96A03 /* SARemoteConfigCommonOperator.h in Headers */, 4DD1285A25F872A4008C0B1E /* SAVisualizedObjectSerializerManager.h in Headers */, + FCC02F2A26CE4EF700DB8F54 /* SAUserAgent.h in Headers */, 881A4197253D7B4F00854F69 /* SASwizzle.h in Headers */, 4D2D537E2591EB0600805141 /* SALoggerPrePostFixFormatter.h in Headers */, 881A41FD253D7B5000854F69 /* SAObject+SAConfigOptions.h in Headers */, @@ -1763,13 +1813,16 @@ 881A4196253D7B4F00854F69 /* SAConstants.h in Headers */, 4DD1286425F872A4008C0B1E /* SAVisualizedAutoTrackObjectSerializer.h in Headers */, A8BCC4AB2686C42D00B72040 /* SASecretKey.h in Headers */, - A8BCC4D926872A3F00B72040 /* SADeeplinkManager.h in Headers */, + A8BCC4D926872A3F00B72040 /* SADeepLinkManager.h in Headers */, F28997D7273B6D66005E7D5E /* SAGesturePlugin.h in Headers */, 881A419C253D7B4F00854F69 /* SAConstants+Private.h in Headers */, 8876234C26E61AD80067F0B4 /* SAPropertyPluginManager.h in Headers */, 883ED1A32768AF0900A0706A /* SAAESCrypt.h in Headers */, 881A419F253D7B4F00854F69 /* SAHTTPSession.h in Headers */, F2E9723125E637820009A2B9 /* SAAppPushManager.h in Headers */, + FCBECDF727DEDF6400361D6C /* SADeepLinkEventProcessor.h in Headers */, + F277F5E425CFCE56009B5CE6 /* NSObject+DelegateProxy.h in Headers */, + F277F5C325CF9A43009B5CE6 /* UIApplication+PushClick.h in Headers */, F277F5E425CFCE56009B5CE6 /* NSObject+SADelegateProxy.h in Headers */, F277F5C325CF9A43009B5CE6 /* UIApplication+SAPushClick.h in Headers */, 4DD128B925F8A003008C0B1E /* SensorsAnalyticsSDK+Visualized.h in Headers */, @@ -1777,6 +1830,7 @@ 88EC2DFF2757693600EF9778 /* SAUserDefaultsStorePlugin.h in Headers */, 4DD1286525F872A4008C0B1E /* SAObjectSerializerConfig.h in Headers */, F27EA3CD2739068C00896B3A /* SAEventTrackerPluginManager.h in Headers */, + FC0A8C63276334F200109267 /* SADeepLinkConstants.h in Headers */, 4DD1286C25F872A4008C0B1E /* SAClassDescription.h in Headers */, A8356DC82656459A00FD64AA /* SAScrollViewDelegateProxy.h in Headers */, A8356DBE2656459A00FD64AA /* UIView+SAAutoTrack.h in Headers */, @@ -1958,6 +2012,7 @@ 4D4DB31B25B7D58C00938842 /* SAEventStore.m in Sources */, A82E894E267D918100475757 /* SASecretKeyFactory.m in Sources */, 4D4DB31725B7D58300938842 /* SADateFormatter.m in Sources */, + FC0A8C64276334F200109267 /* SADeepLinkConstants.m in Sources */, 45A5655D263C174300C9C41B /* SAIdentifier.m in Sources */, 4D4DB31325B7D55400938842 /* SAEventTracker.m in Sources */, 4D4DB30F25B7D54D00938842 /* SALog.m in Sources */, @@ -1978,6 +2033,7 @@ 4D4DB2FB25B7D4D800938842 /* SAURLUtils.m in Sources */, 881A4228253D7B5E00854F69 /* SALocationManager.m in Sources */, A8356DDD2656459A00FD64AA /* SAAutoTrackUtils.m in Sources */, + FCC02F2B26CE4EF700DB8F54 /* SAUserAgent.m in Sources */, F2E4ABAA26ECB19200BA7F01 /* SensorsAnalyticsSDK+DebugMode.m in Sources */, 4D2D53BD2591EB3A00805141 /* SAReadWriteLock.m in Sources */, 4D2D539D2591EB2100805141 /* SAEventFlush.m in Sources */, @@ -1996,6 +2052,7 @@ 4DD128C425F8A003008C0B1E /* SAVisualPropertiesConfigSources.m in Sources */, F277F5E325CFCE56009B5CE6 /* NSObject+SADelegateProxy.m in Sources */, 4D2D53842591EB0600805141 /* SAFileLogger.m in Sources */, + FC332DE827672606009122FC /* SADeepLinkProcessor.m in Sources */, 4DD1296925F8E451008C0B1E /* UIView+SAVisualProperties.m in Sources */, 881A41E4253D7B5000854F69 /* SAKeyChainItemWrapper.m in Sources */, 4D41D9D425FF7E9300D856F4 /* UIViewController+SAElementPath.m in Sources */, @@ -2013,6 +2070,7 @@ A8CC22352685E50C00E96A03 /* SARemoteConfigCommonOperator.m in Sources */, 4D9B536826382F0100318B1D /* SADeviceOrientationManager.m in Sources */, 4DD1285425F872A4008C0B1E /* SAEnumDescription.m in Sources */, + FCBECDF827DEDF6400361D6C /* SADeepLinkEventProcessor.m in Sources */, 4D2D53BE2591EB3A00805141 /* SAJSONUtil.m in Sources */, F2E4ABA226ECAA8600BA7F01 /* SensorsAnalyticsSDK+DeviceOrientation.m in Sources */, A82E895E267D918100475757 /* SARSAPluginEncryptor.m in Sources */, @@ -2034,13 +2092,16 @@ A8BCC4DF26872B6600B72040 /* SADebugModeManager.m in Sources */, A8356DBF2656459A00FD64AA /* UIViewController+SAAutoTrack.m in Sources */, 45A5656B263C174300C9C41B /* SATrackEventObject.m in Sources */, + A8356DC42656459A00FD64AA /* UIApplication+AutoTrack.m in Sources */, + FCBECE0027DEDF7F00361D6C /* SADeferredDeepLinkProcessor.m in Sources */, A8356DC42656459A00FD64AA /* UIApplication+SAAutoTrack.m in Sources */, F23CA0072701715E002EEACA /* SAJavaScriptBridgeManager.m in Sources */, F2066972271FFEE10002ABDF /* UIViewController+SAPageLeave.m in Sources */, A8356DD22656459A00FD64AA /* SAGestureTargetActionModel.m in Sources */, 883ED1AA27699A3500A0706A /* SABaseStoreManager.m in Sources */, F27EA3D727393B4B00896B3A /* SACellClickDynamicSubclassPlugin.m in Sources */, - A8BCC4DA26872A3F00B72040 /* SADeeplinkManager.m in Sources */, + FCBECDF427DEDF4200361D6C /* SAQueryDeepLinkProcessor.m in Sources */, + A8BCC4DA26872A3F00B72040 /* SADeepLinkManager.m in Sources */, A8356DBC2656459A00FD64AA /* SAAutoTrackManager.m in Sources */, 883ED1A12768AF0900A0706A /* SAAESCrypt.m in Sources */, 45A56565263C174300C9C41B /* SAEventLibObject.m in Sources */, @@ -2080,6 +2141,7 @@ A8356DD72656459A00FD64AA /* SAAppClickTracker.m in Sources */, 881A4202253D7B5000854F69 /* SAModuleManager.m in Sources */, F2E9723225E637820009A2B9 /* SAAppPushManager.m in Sources */, + FCBECDFC27DEDF7200361D6C /* SARequestDeepLinkProcessor.m in Sources */, 881A41FC253D7B5000854F69 /* SASwizzle.m in Sources */, F28997D8273B6D66005E7D5E /* SAGesturePlugin.m in Sources */, 881A4207253D7B5000854F69 /* SAHTTPSession.m in Sources */, @@ -2091,7 +2153,7 @@ 45A5656D263C174300C9C41B /* SAPresetProperty.m in Sources */, 4DD1285E25F872A4008C0B1E /* SAVisualizedObjectSerializerManager.m in Sources */, 45A5655E263C174300C9C41B /* SASuperProperty.m in Sources */, - F2E4ABA626ECAC5400BA7F01 /* SensorsAnalyticsSDK+Deeplink.m in Sources */, + F2E4ABA626ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.m in Sources */, 4DD1286D25F872A4008C0B1E /* SAVisualizedManager.m in Sources */, 4DD1286A25F872A4008C0B1E /* SAWebElementView.m in Sources */, F277F5BF25CF9A43009B5CE6 /* SAApplicationDelegateProxy.m in Sources */, diff --git a/SensorsAnalyticsSDK/AutoTrack/AppStart/SAAppStartTracker.m b/SensorsAnalyticsSDK/AutoTrack/AppStart/SAAppStartTracker.m index 29c1ed08..59c56206 100644 --- a/SensorsAnalyticsSDK/AutoTrack/AppStart/SAAppStartTracker.m +++ b/SensorsAnalyticsSDK/AutoTrack/AppStart/SAAppStartTracker.m @@ -71,7 +71,7 @@ - (void)autoTrackEventWithProperties:(NSDictionary *)properties { eventProperties[kSAEventPropertyAppFirstStart] = self.isRelaunch ? @(NO) : @([self isFirstAppStart]); eventProperties[kSAEventPropertyResumeFromBackground] = self.isRelaunch ? @(YES) : @(NO); } - //添加 deeplink 相关渠道信息,可能不存在 + //添加 deepLink 相关渠道信息,可能不存在 [eventProperties addEntriesFromDictionary:properties]; [self trackAutoTrackEventWithProperties:eventProperties]; diff --git a/SensorsAnalyticsSDK/AutoTrack/AppViewScreen/SAAppViewScreenTracker.m b/SensorsAnalyticsSDK/AutoTrack/AppViewScreen/SAAppViewScreenTracker.m index 92d1dbb7..9ee4ced9 100644 --- a/SensorsAnalyticsSDK/AutoTrack/AppViewScreen/SAAppViewScreenTracker.m +++ b/SensorsAnalyticsSDK/AutoTrack/AppViewScreen/SAAppViewScreenTracker.m @@ -138,7 +138,7 @@ - (NSDictionary *)buildWithViewController:(UIViewController *)viewController pro [eventProperties addEntriesFromDictionary:autoTrackProperties]; if (autoTrack) { - // App 通过 Deeplink 启动时第一个页面浏览事件会添加 utms 属性 + // App 通过 DeepLink 启动时第一个页面浏览事件会添加 utms 属性 // 只需要处理全埋点的页面浏览事件 [eventProperties addEntriesFromDictionary:SAModuleManager.sharedInstance.utmProperties]; [SAModuleManager.sharedInstance clearUtmProperties]; diff --git a/SensorsAnalyticsSDK/CAID/SACAIDUtils.m b/SensorsAnalyticsSDK/CAID/SACAIDUtils.m deleted file mode 100644 index 6f894cee..00000000 --- a/SensorsAnalyticsSDK/CAID/SACAIDUtils.m +++ /dev/null @@ -1,96 +0,0 @@ -// -// SACAIDUtils.m -// SensorsAnalyticsSDK -// -// Created by 彭远洋 on 2021/3/4. -// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#if ! __has_feature(objc_arc) -#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. -#endif - -#import "SACAIDUtils.h" -#import "SASwizzle.h" -#import "SAStoreManager.h" -#import "SALog.h" - -static NSString *const kSACAIDCacheKey = @"com.sensorsdata.caid.cache"; -static NSDictionary *caid; - -@implementation SACAIDUtils - -+ (NSDictionary *)CAIDInfo { - Class CAID = NSClassFromString(@"CAID"); - if (!CAID) { - SALogError(@"您未集成 CAID SDK,请按照集成文档正确集成 CAID SDK 后调用 getCAIDAsyncly 接口获取 CAID 信息"); - return nil; - } - if (!caid) { - caid = [[SAStoreManager sharedInstance] objectForKey:kSACAIDCacheKey]; - } - if (caid.allKeys.count == 0 ) { - SALogError(@"未获取到缓存的 CAID 信息,请检查是否正确集成 CAID SDK,并调用 getCAIDAsyncly 接口获取 CAID 信息"); - return nil; - } - return caid; -} - -#pragma mark - swizzled Method -+ (void)load { - Class CAID = NSClassFromString(@"CAID"); - if (!CAID) { - NSAssert(CAID, @"您未集成 CAID SDK,请按照集成文档正确集成 CAID SDK 后调用 getCAIDAsyncly 接口获取 CAID 信息"); - return; - } - SEL origSel = NSSelectorFromString(@"getCAIDAsyncly:"); - SEL swizzledSel = NSSelectorFromString(@"sensorsdata_getCAIDAsyncly:"); - [CAID sa_swizzleMethod:origSel withClass:SACAIDUtils.class withMethod:swizzledSel error:nil]; -} - -- (void)sensorsdata_getCAIDAsyncly:(void(^)(id error, id caidStruct))callback { - [self sensorsdata_getCAIDAsyncly:^(id _Nonnull error, id _Nonnull caidStruct) { - NSInteger code = [SACAIDUtils invokeError:error selString:@"code"]; - if (code == 0) { - // CAIDErrorCodeNone = 0, 无错误 - NSMutableDictionary *caid = [NSMutableDictionary dictionary]; - caid[@"caid"] = (NSString *)[SACAIDUtils invokeObject:caidStruct selString:@"caid"]; - caid[@"caid_version"] = (NSNumber *)[SACAIDUtils invokeObject:caidStruct selString:@"version"]; - caid[@"last_caid"] = (NSString *)[SACAIDUtils invokeObject:caidStruct selString:@"lastVersionCAID"]; - caid[@"last_caid_version"] = (NSNumber *)[SACAIDUtils invokeObject:caidStruct selString:@"lastVersion"]; - // 客户每次调用 getCAIDAsyncly 方法成功后都会更新本地 CAID 信息 - [[SAStoreManager sharedInstance] setObject:caid forKey:kSACAIDCacheKey]; - } - callback(error, caidStruct); - }]; -} - -+ (NSInteger)invokeError:(id)obj selString:(NSString *)selString { - SEL sel = NSSelectorFromString(selString); - if ([obj respondsToSelector:sel]) { - return ((NSInteger (*)(id, SEL))[obj methodForSelector:sel])(obj, sel); - } - return -1; -} - -+ (id)invokeObject:(id)obj selString:(NSString *)selString { - SEL sel = NSSelectorFromString(selString); - if ([obj respondsToSelector:sel]) { - return ((id (*)(id, SEL))[obj methodForSelector:sel])(obj, sel); - } - return nil; -} - -@end diff --git a/SensorsAnalyticsSDK/ChannelMatch/SAChannelMatchManager.m b/SensorsAnalyticsSDK/ChannelMatch/SAChannelMatchManager.m index 33951826..d8eaa59f 100644 --- a/SensorsAnalyticsSDK/ChannelMatch/SAChannelMatchManager.m +++ b/SensorsAnalyticsSDK/ChannelMatch/SAChannelMatchManager.m @@ -141,7 +141,7 @@ - (BOOL)isValidForChannelDebug { /// 当前获取到的设备 ID 为有效值 - (BOOL)isValidOfDeviceInfo { - return ([SAIdentifier idfa].length > 0 || [self CAIDInfo].allKeys > 0); + return [SAIdentifier idfa].length > 0; } - (BOOL)isTrackedAppInstallWithDisableCallback:(BOOL)disableCallback { @@ -217,7 +217,6 @@ - (NSDictionary *)profileProperties:(NSDictionary *)properties { - (NSString *)appInstallSource { NSMutableDictionary *sources = [NSMutableDictionary dictionary]; - [sources addEntriesFromDictionary:[self CAIDInfo]]; sources[@"idfa"] = [SAIdentifier idfa]; sources[@"idfv"] = [SAIdentifier idfv]; NSMutableArray *result = [NSMutableArray array]; @@ -227,15 +226,6 @@ - (NSString *)appInstallSource { return [result componentsJoinedByString:@"##"]; } -- (NSDictionary *)CAIDInfo { - Class cla = NSClassFromString(@"SACAIDUtils"); - SEL sel = NSSelectorFromString(@"CAIDInfo"); - if ([cla respondsToSelector:sel]) { - return ((NSDictionary * (*)(id, SEL))[cla methodForSelector:sel])(cla, sel); - } - return nil; -} - #pragma mark - 附加渠道信息 - (void)trackChannelWithEventObject:(SABaseEventObject *)obj properties:(nullable NSDictionary *)propertyDict { if (self.configOptions.enableAutoAddChannelCallbackEvent) { @@ -333,8 +323,6 @@ - (void)showRelinkAlertWithURL:(NSURL *)url { NSMutableSet *deviceIdSet = [NSMutableSet setWithArray:[deviceId componentsSeparatedByString:@"##"]]; // 当前设备的设备信息 NSSet *installSourceSet = [NSSet setWithArray:[[self appInstallSource] componentsSeparatedByString:@"##"]]; - // 当 IDFV 、IDFA caid、last_caid 都不一致,且只有 caid_version 一致时会出现匹配错误的情况 - // 此场景在实际业务中出现概率较低,不考虑此问题 [deviceIdSet intersectSet:installSourceSet]; // 取交集,当交集不为空时,表示设备一致 if (deviceIdSet.count > 0) { @@ -431,7 +419,7 @@ - (void)showChannelDebugInstall { #pragma mark - Error Message - (void)showChannelDebugErrorMessage { NSString *title = @"检测到“设备码为空”,可能的原因如下,请排查:"; - NSString *content = @"\n1. 手机系统设置中「隐私->广告-> 限制广告追踪」;\n\n2.若手机系统为 iOS 14 ,请联系研发人员确认 trackAppInstall 接口是否在 “跟踪” 授权之后调用。\n\n排查修复后,请重新扫码进行联调。\n\n3. 若集成了 CAID SDK,请联系研发人员确认 trackAppInstall 接口是否在 “getCAIDAsyncly” 授权之后调用。\n\n"; + NSString *content = @"\n1. 手机系统设置中「隐私->广告-> 限制广告追踪」;\n\n2.若手机系统为 iOS 14 ,请联系研发人员确认 trackAppInstall 接口是否在 “跟踪” 授权之后调用。\n\n排查修复后,请重新扫码进行联调。\n\n"; SAAlertController *alertController = [[SAAlertController alloc] initWithTitle:title message:content preferredStyle:SAAlertControllerStyleAlert]; [alertController addActionWithTitle:@"确认" style:SAAlertActionStyleCancel handler:nil]; [alertController show]; diff --git a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h index fd3039c8..1284d6db 100644 --- a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h +++ b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN extern NSString * const kSAIdentitiesLoginId; +extern NSString * const kSALoginIdSpliceKey; @interface SAIdentifier : NSObject @@ -38,17 +39,16 @@ extern NSString * const kSAIdentitiesLoginId; /// ID-Mapping 3.0 业务 ID,当前所有处理逻辑都是在的 serialQueue 中处理 @property (nonatomic, copy, readonly) NSDictionary *identities; -/// 自定义的 loginIDKey, +/// 自定义的 loginIDKey @property (nonatomic, copy, readonly) NSString *loginIDKey; /** 初始化方法 @param queue 一个全局队列 - @param loginIDKey 自定义的 loginIDKey @return 初始化对象 */ -- (instancetype)initWithQueue:(dispatch_queue_t)queue loginIDKey:(NSString *)loginIDKey; +- (instancetype)initWithQueue:(dispatch_queue_t)queue; /** 自定义匿名 Id(设备 Id) @@ -64,26 +64,30 @@ extern NSString * const kSAIdentitiesLoginId; */ - (void)resetAnonymousId; +#pragma mark - Login /** -检查传入的 loginId 合法性 +检查登录时参数的合法性 - @param loginId 设置的 loginId + @param key 设置的 loginIDKey + @param value 设置的 loginId @return 合法性结果 */ -- (BOOL)isValidLoginId:(NSString *)loginId; +- (BOOL)isValidForLogin:(NSString *)key value:(NSString *)value; /** - 通过登录接口设置 loginId + 通过登录接口设置 loginIDKey 和 loginId + @param key 新的 loginIDKey @param loginId 新的 loginId */ -- (void)login:(NSString *)loginId; +- (void)loginWithKey:(NSString *)key loginId:(NSString *)loginId; /** 通过退出登录接口删除本地的 loginId */ - (void)logout; +#pragma mark - Device ID /** 获取设备的 IDFA @@ -107,10 +111,13 @@ extern NSString * const kSAIdentitiesLoginId; #pragma mark - Identities -/// 检查添业务 ID 是否有效,用于触发事件前判断 -- (BOOL)isValidIdentity:(NSString *)key value:(NSString *)value; +/// 检查绑定业务 ID 是否有效,用于触发事件前判断 +- (BOOL)isValidForBind:(NSString *)key value:(NSString *)value; -/// 绑定业务 ID,需要在 serialQueue 中调用,同时绑定多个 ID 时数据 +/// 检查解绑业务 ID 是否有效,用于触发事件前判断 +- (BOOL)isValidForUnbind:(NSString *)key value:(NSString *)value; + +/// 绑定业务 ID,需要在 serialQueue 中调用 - (void)bindIdentity:(NSString *)key value:(NSString *)value; /// 解绑业务 ID,需要在 serialQueue 中调用 diff --git a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m index d834fc5a..c245b7a8 100644 --- a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m +++ b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m @@ -51,6 +51,8 @@ NSString * const kSALoginIDKey = @"com.sensorsdata.loginidkey"; NSString * const kSAIdentitiesCacheType = @"Base64:"; +NSString * const kSALoginIdSpliceKey = @"+"; + @interface SAIdentifier () @property (nonatomic, strong) dispatch_queue_t queue; @@ -59,6 +61,9 @@ @interface SAIdentifier () @property (nonatomic, copy, readwrite) NSString *anonymousId; @property (nonatomic, copy, readwrite) NSString *loginIDKey; +// ID-Mapping 3.0 拼接前客户传入的原始 LoginID +@property (nonatomic, copy) NSString *originalLoginId; + @property (nonatomic, copy, readwrite) NSDictionary *identities; @property (nonatomic, copy) NSDictionary *removedIdentity; @@ -68,32 +73,23 @@ @implementation SAIdentifier #pragma mark - Life Cycle -- (instancetype)initWithQueue:(dispatch_queue_t)queue loginIDKey:(NSString *)loginIDKey { +- (instancetype)initWithQueue:(dispatch_queue_t)queue { self = [super init]; if (self) { _queue = queue; - - NSError *error = nil; - [SAValidator validKey:loginIDKey error:&error]; - if (error) { - SALogError(@"%@",error.localizedDescription); - if (error.code != SAValidatorErrorOverflow) { - loginIDKey = kSAIdentitiesLoginId; - } - } - - if (![loginIDKey isEqualToString:kSAIdentitiesLoginId] && [self isPresetKey:loginIDKey]) { - SALogError(@"LoginIDKey [ %@ ] is invalid", loginIDKey); - loginIDKey = kSAIdentitiesLoginId; - } - _loginIDKey = loginIDKey; - dispatch_async(_queue, ^{ // 获取 self.identities 需要判断当前本地文件是否存在 anonymousId // 获取 self.anonymousId 会写入本地文件,因此需要先获取 self.identities - self.identities = [self unarchiveIdentities]; + self.loginIDKey = [self unarchiveLoginIDKey]; + self.identities = [self unarchiveIdentitiesWithKey:self.loginIDKey]; self.anonymousId = [self unarchiveAnonymousId]; - self.loginId = [[SAStoreManager sharedInstance] objectForKey:kSAEventLoginId]; + NSString *cacheLoginId = [[SAStoreManager sharedInstance] objectForKey:kSAEventLoginId]; + self.originalLoginId = cacheLoginId; + if ([self.loginIDKey isEqualToString:kSAIdentitiesLoginId]) { + self.loginId = cacheLoginId; + } else { + self.loginId = [NSString stringWithFormat:@"%@%@%@", self.loginIDKey, kSALoginIdSpliceKey, cacheLoginId]; + } }); } return self; @@ -157,58 +153,83 @@ - (BOOL)isValidLoginId:(NSString *)loginId { SALogError(@"LoginId is empty"); return NO; } - if ([loginId length] > kSAPropertyValueMaxLength) { SALogWarn(@"LoginId: %@'s length is longer than %ld", loginId, kSAPropertyValueMaxLength); } - - if ([loginId isEqualToString:self.loginId]) { - return NO; - } - // 为了避免将匿名 ID 作为 LoginID 传入 if ([loginId isEqualToString:self.anonymousId]) { return NO; } + return YES; +} - NSString *cachedKey = [[SAStoreManager sharedInstance] objectForKey:kSALoginIDKey]; - // 当 loginIDKey 发生变化时,不需要检查 loginId 是否相同 - if ([cachedKey isEqualToString:self.loginIDKey] && [loginId isEqualToString:self.loginId]) { +- (BOOL)isValidLoginIDKey:(NSString *)key { + NSError *error = nil; + [SAValidator validKey:key error:&error]; + if (error) { + SALogError(@"%@",error.localizedDescription); + if (error.code != SAValidatorErrorOverflow) { + return NO; + } + } + if ([self isDeviceIDKey:key] || [self isAnonymousIDKey:key]) { + SALogError(@"LoginIDKey [ %@ ] is invalid", key); return NO; } - return YES; } -- (void)login:(NSString *)loginId { - [self updateLoginId:loginId]; - [self bindIdentity:self.loginIDKey value:loginId]; +- (BOOL)isValidForLogin:(NSString *)key value:(NSString *)value { + if (![self isValidLoginIDKey:key]) { + return NO; + } + if (![self isValidLoginId:value]) { + return NO; + } + // 当 loginIDKey 和 loginId 均未发生变化时,不需要触发事件 + if ([self.loginIDKey isEqualToString:key] && [self.originalLoginId isEqualToString:value]) { + return NO; + } + return YES; +} + +- (void)loginWithKey:(NSString *)key loginId:(NSString *)loginId { + [self updateLoginInfo:key loginId:loginId]; + [self bindIdentity:key value:loginId]; } -- (void)updateLoginId:(NSString *)loginId { +- (void)updateLoginInfo:(NSString *)loginIDKey loginId:(NSString *)loginId { dispatch_async(self.queue, ^{ - self.loginId = loginId; + if ([loginIDKey isEqualToString:kSAIdentitiesLoginId]) { + self.loginId = loginId; + } else { + self.loginId = [NSString stringWithFormat:@"%@%@%@", loginIDKey, kSALoginIdSpliceKey,loginId]; + } + self.originalLoginId = loginId; + self.loginIDKey = loginIDKey; + // 本地缓存的 login_id 值为原始值,在初始化时处理拼接逻辑 [[SAStoreManager sharedInstance] setObject:loginId forKey:kSAEventLoginId]; // 登录时本地保存当前的 loginIDKey 字段,字段存在时表示 v3.0 版本 SDK 已进行过登录 - [[SAStoreManager sharedInstance] setObject:self.loginIDKey forKey:kSALoginIDKey]; + [[SAStoreManager sharedInstance] setObject:loginIDKey forKey:kSALoginIDKey]; }); } - (void)logout { - [self clearLoginId]; + [self clearLoginInfo]; [self resetIdentities]; } -- (void)clearLoginId { +- (void)clearLoginInfo { dispatch_async(self.queue, ^{ self.loginId = nil; + self.originalLoginId = nil; + self.loginIDKey = kSAIdentitiesLoginId; [[SAStoreManager sharedInstance] removeObjectForKey:kSAEventLoginId]; // 退出登录时清除本地保存的 loginIDKey 字段,字段不存在时表示 v3.0 版本 SDK 已退出登录 [[SAStoreManager sharedInstance] removeObjectForKey:kSALoginIDKey]; }); } - #if TARGET_OS_IOS + (NSString *)idfa { Class cla = NSClassFromString(@"SAIDFAHelper"); @@ -305,6 +326,14 @@ - (NSString *)loginId { return loginId; } +- (NSString *)originalLoginId { + __block NSString *originalLoginId; + sensorsdata_dispatch_safe_sync(self.queue, ^{ + originalLoginId = _originalLoginId; + }); + return originalLoginId; +} + - (NSString *)anonymousId { __block NSString *anonymousId; sensorsdata_dispatch_safe_sync(self.queue, ^{ @@ -336,6 +365,14 @@ - (NSDictionary *)identities { return identities; } +- (NSString *)loginIDKey { + __block NSString *loginIDKey; + sensorsdata_dispatch_safe_sync(self.queue, ^{ + loginIDKey = _loginIDKey; + }); + return loginIDKey; +} + - (NSDictionary *)removedIdentity { __block NSDictionary *removedIdentity; sensorsdata_dispatch_safe_sync(self.queue, ^{ @@ -348,7 +385,7 @@ - (NSDictionary *)removedIdentity { - (NSDictionary *)mergeH5Identities:(NSDictionary *)identities eventType:(NSString *)eventType { if ([eventType isEqualToString:kSAEventTypeUnbind]) { NSString *key = identities.allKeys.firstObject; - if (![self isValidIdentity:key value:identities[key]]) { + if (![self isValidForUnbind:key value:identities[key]]) { return @{}; } [self unbindIdentity:key value:identities[key]]; @@ -381,20 +418,46 @@ - (NSDictionary *)mergeH5Identities:(NSDictionary *)identities eventType:(NSStri return newIdentities; } -- (BOOL)isPresetKey:(NSString *)key { - // IDFV 和 ios_uuid 为 SDK 生成的设备唯一标识,不允许客户绑定与解绑 +- (BOOL)isDeviceIDKey:(NSString *)key { + return ([key isEqualToString:kSAIdentitiesUniqueID] || + [key isEqualToString:kSAIdentitiesUUID] + #if TARGET_OS_OSX + || [key isEqualToString:kSAIdentitiesOldUniqueID] + #endif + ); +} + +- (BOOL)isAnonymousIDKey:(NSString *)key { // $identity_anonymous_id 为兼容 2.0 identify() 的产物,也不允许客户绑定与解绑 + return [key isEqualToString:kSAIdentitiesAnonymousId]; +} + +- (BOOL)isLoginIDKey:(NSString *)key { // $identity_login_id 为业务唯一标识,不允许客户绑定或解绑,只能通过 login 接口关联 - return ([key isEqualToString:kSAIdentitiesUniqueID] || - [key isEqualToString:kSAIdentitiesUUID] || - [key isEqualToString:kSAIdentitiesLoginId] || -#if TARGET_OS_OSX - [key isEqualToString:kSAIdentitiesOldUniqueID] || -#endif - [key isEqualToString:kSAIdentitiesAnonymousId]); + return [key isEqualToString:kSAIdentitiesLoginId]; } -- (BOOL)isValidIdentity:(NSString *)key value:(NSString *)value { +- (BOOL)isValidForBind:(NSString *)key value:(NSString *)value { + if (![key isKindOfClass:NSString.class]) { + SALogError(@"Key [%@] must be string", key); + return NO; + } + if (key.length <= 0) { + SALogError(@"Key is empty"); + return NO; + } + if ([self isDeviceIDKey:key] || [self isAnonymousIDKey:key] || [self isLoginIDKey:key]) { + SALogError(@"Key [ %@ ] is invalid", key); + return NO; + } + if ([key isEqualToString:self.loginIDKey]) { + SALogError(@"Key [ %@ ] is invalid", key); + return NO; + } + return [self isValidIdentity:key value:value]; +} + +- (BOOL)isValidForUnbind:(NSString *)key value:(NSString *)value { if (![key isKindOfClass:NSString.class]) { SALogError(@"Key [%@] must be string", key); return NO; @@ -403,6 +466,10 @@ - (BOOL)isValidIdentity:(NSString *)key value:(NSString *)value { SALogError(@"Key is empty"); return NO; } + return [self isValidIdentity:key value:value]; +} + +- (BOOL)isValidIdentity:(NSString *)key value:(NSString *)value { NSError *error = nil; [SAValidator validKey:key error:&error]; if (error) { @@ -411,11 +478,8 @@ - (BOOL)isValidIdentity:(NSString *)key value:(NSString *)value { if (error && error.code != SAValidatorErrorOverflow) { return NO; } - if ([self isPresetKey:key]) { - SALogError(@"Key [ %@ ] is invalid", key); - return NO; - } - if ([key isEqualToString:self.loginIDKey]) { + // 不允许绑定/解绑 $identity_anonymous_id 和 $identity_login_id + if ([self isAnonymousIDKey:key] || [self isLoginIDKey:key]) { SALogError(@"Key [ %@ ] is invalid", key); return NO; } @@ -447,13 +511,20 @@ - (void)bindIdentity:(NSString *)key value:(NSString *)value { - (void)unbindIdentity:(NSString *)key value:(NSString *)value { NSMutableDictionary *removed = [NSMutableDictionary dictionary]; removed[key] = value; - if (![value isEqualToString:self.identities[key]]) { + if (![value isEqualToString:self.identities[key]] || [self isDeviceIDKey:key]) { // 当 identities 中不存在需要解绑的字段时,不需要进行删除操作 dispatch_async(self.queue, ^{ self.removedIdentity = removed; }); return; } + + // 当解绑自定义 loginIDKey 时,需要同步清除 2.0 的 login_id 信息 + NSString *result = [NSString stringWithFormat:@"%@%@%@", key, kSALoginIdSpliceKey, value]; + if ([result isEqualToString:self.loginId]) { + [self clearLoginInfo]; + } + NSMutableDictionary *identities = [self.identities mutableCopy]; [identities removeObjectForKey:key]; dispatch_async(self.queue, ^{ @@ -468,7 +539,7 @@ - (void)resetIdentities { identities[kSAIdentitiesUniqueID] = self.identities[kSAIdentitiesUniqueID]; identities[kSAIdentitiesUUID] = self.identities[kSAIdentitiesUUID]; // 当 loginId 存在时需要添加 loginId - identities[self.loginIDKey] = self.loginId; + identities[self.loginIDKey] = self.originalLoginId; dispatch_async(self.queue, ^{ self.identities = identities; [self archiveIdentities:identities]; @@ -490,7 +561,16 @@ - (NSDictionary *)identitiesWithEventType:(NSString *)eventType { return identities; } -- (NSDictionary *)unarchiveIdentities { +- (NSString *)unarchiveLoginIDKey { + NSString *content = [[SAStoreManager sharedInstance] objectForKey:kSALoginIDKey]; + if (content.length < 1) { + content = kSAIdentitiesLoginId; + [[SAStoreManager sharedInstance] setObject:content forKey:kSALoginIDKey]; + } + return content; +} + +- (NSDictionary *)unarchiveIdentitiesWithKey:(NSString *)loginIDKey { NSDictionary *cache = [self decodeIdentities]; NSMutableDictionary *identities = [NSMutableDictionary dictionaryWithDictionary:cache]; @@ -533,27 +613,14 @@ - (NSDictionary *)unarchiveIdentities { NSString *loginId = [[SAStoreManager sharedInstance] objectForKey:kSAEventLoginId]; // 本地存在 loginId 时表示 v2.0 版本为登录状态,可能需要将登录状态同步 v3.0 版本的 identities 中 // 为了避免客户升级 v3.0 后又降级至 v2.0,然后又升级至 v3.0 版本的兼容问题,这里每次冷启动都处理一次 - if (loginId) { - // 当 v3.0 版本进行过登录操作时,本地一定会存在登录时使用的 loginIDKey 内容 - NSString *cachedKey = [[SAStoreManager sharedInstance] objectForKey:kSALoginIDKey]; - // 场景 1: - // v3.0 版本设置 loginIDKey 为 a_id 并进行登录 123, 降级至 v2.0 版本并重新登录 456, 再次升级至 v3.0 版本后 loginIDKey 仍为 a_id - // 此时 identities 中存在 a_id 内容,需要更新 a_id 内容 - - // 场景 2: - // v3.0 版本设置 loginIDKey 为 a_id 并进行登录 123, 降级至 v2.0 版本并重新登录 456, 再次升级至 v3.0 版本后设置 loginIDKey 为 b_id - // 此时 identities 中存在 a_id 内容且不存在 b_id 内容,因此只需要更新 a_id 内容 - - // 场景 3: - // v3.0 版本设置 loginIDKey 为 a_id 且未进行登录, 降级至 v2.0 版本并重新登录 456, 再次升级至 v3.0 版本后 loginIDKey 仍为 a_id - // 此时 identities 中不存在 cacheKey 对应内容,表示 v3.0 版本未进行过登录操作。要将 v2.0 版本登录状态 { a_id:456 } 同步至 v3.0 版本的 identities 中 - - // 场景 4: - // v3.0 版本设置 loginIDKey 为 a_id 且未进行登录, 降级至 v2.0 版本并重新登录 456, 再次升级至 v3.0 版本后设置 loginIDKey 为 b_id - // 此时 identities 中不存在 cacheKey 对应内容,表示 v3.0 版本未进行过登录操作。要将 v2.0 版本登录状态 { b_id:456 } 同步至 v3.0 版本的 identities 中 + // 当 v3.0 版本进行过登录操作时,本地一定会存在登录时使用的 loginIDKey 内容 + NSString *cachedKey = [[SAStoreManager sharedInstance] objectForKey:kSALoginIDKey]; + if (loginId) { if (identities[cachedKey]) { - // 场景 1 和 场景 2 对应逻辑 + // 场景: + // v3.0 版本设置 loginIDKey 为 a_id 并进行登录 123, 降级至 v2.0 版本并重新登录 456, 再次升级至 v3.0 版本后 loginIDKey 仍为 a_id + // 此时 identities 中存在 a_id 内容,需要更新 a_id 内容 if (![identities[cachedKey] isEqualToString:loginId]) { // 当 identities 中 cachedKey 内容和 v2.0 版本 loginId 内容不一致时,表示登录用户发生了变化,需要更新 cachedKey 对应内容并清空其他所有业务 ID NSMutableDictionary *newIdentities = [NSMutableDictionary dictionary]; @@ -564,20 +631,19 @@ - (NSDictionary *)unarchiveIdentities { identities = newIdentities; } } else { - // 场景 3 和 场景 4 对应逻辑 - // 当 identities 中不存在 cacheKey 内容时,即表示 v3.0 版本未进行过登录,需要同步 v2.0 版本登录状态至 v3.0 版本 identities 中 + // 场景: + // v3.0 版本设置 loginIDKey 为 $identity_login_id 且未进行登录, 降级至 v2.0 版本并重新登录 456, 再次升级至 v3.0 版本后 loginIDKey 仍为 $identity_login_id + // 此时 identities 中不存在 cacheKey 对应内容,表示 v3.0 版本未进行过登录操作。要将 v2.0 版本登录状态 { $identity_login_id:456 } 同步至 v3.0 版本的 identities 中 NSMutableDictionary *newIdentities = [NSMutableDictionary dictionary]; newIdentities[kSAIdentitiesUniqueID] = identities[kSAIdentitiesUniqueID]; newIdentities[kSAIdentitiesUUID] = identities[kSAIdentitiesUUID]; - newIdentities[self.loginIDKey] = loginId; + newIdentities[loginIDKey] = loginId; identities = newIdentities; // 此时相当于进行登录操作,需要保存登录时设置的 loginIDKey 内容至本地文件中 - [[SAStoreManager sharedInstance] setObject:self.loginIDKey forKey:kSALoginIDKey]; + [[SAStoreManager sharedInstance] setObject:loginIDKey forKey:kSALoginIDKey]; } } else { - NSString *cachedKey = [[SAStoreManager sharedInstance] objectForKey:kSALoginIDKey]; - // 当 identities 中存在 cachedKey 内容时,表示当前 identities 中是登录状态 if (identities[cachedKey]) { // 场景:v3.0 版本登录时,降级至 v2.0 版本并退出登录,然后再升级至 v3.0 版本 // 此时 identities 中仍为登录状态,需要进行退出登录操作 @@ -588,7 +654,6 @@ - (NSDictionary *)unarchiveIdentities { newIdentities[kSAIdentitiesAnonymousId] = identities[kSAIdentitiesAnonymousId]; identities = newIdentities; } - // 当 v2.0 版本状态为未登录状态时,直接清空本地保存的 loginIDKey 文件内容 // v3.0 版本清空本地保存的 loginIDKey 会在 logout 中处理 [[SAStoreManager sharedInstance] removeObjectForKey:kSALoginIDKey]; @@ -607,6 +672,9 @@ - (NSDictionary *)unarchiveIdentities { - (NSDictionary *)decodeIdentities { NSString *content = [[SAStoreManager sharedInstance] objectForKey:kSAIdentities]; + if (![content isKindOfClass:NSString.class]) { + return nil; + } NSData *data; if ([content hasPrefix:kSAIdentitiesCacheType]) { NSString *value = [content substringFromIndex:kSAIdentitiesCacheType.length]; diff --git a/SensorsAnalyticsSDK/Core/SAConfigOptions.h b/SensorsAnalyticsSDK/Core/SAConfigOptions.h index e4b6340b..96a5bfaf 100644 --- a/SensorsAnalyticsSDK/Core/SAConfigOptions.h +++ b/SensorsAnalyticsSDK/Core/SAConfigOptions.h @@ -115,9 +115,6 @@ NS_ASSUME_NONNULL_BEGIN /// App 进入后台时是否等待数据发送结果。默认 NO,不会等待数据发送结果;设置 YES,会等待数据发送结果 @property (nonatomic, assign) BOOL flushBeforeEnterBackground; -/// login 时自定义登录 ID 字段名 -@property (nonatomic, copy) NSString *loginIDKey; - /// 是否进行 session 切割。默认 NO,不会进行 session 切割;设置 YES,会进行 session 切割 @property (nonatomic, assign) BOOL enableSession; diff --git a/SensorsAnalyticsSDK/Core/SAConfigOptions.m b/SensorsAnalyticsSDK/Core/SAConfigOptions.m index b2c420a9..329cdb54 100644 --- a/SensorsAnalyticsSDK/Core/SAConfigOptions.m +++ b/SensorsAnalyticsSDK/Core/SAConfigOptions.m @@ -45,6 +45,9 @@ @interface SAConfigOptions () @property (nonatomic, copy) NSArray *sourceChannels; @property (nonatomic, assign) BOOL enableAutoAddChannelCallbackEvent; +/// 广告相关功能自定义地址 +@property (nonatomic, copy) NSString *customADChannelURL; + @property (nonatomic) BOOL enableJavaScriptBridge; @property (nonatomic, copy) NSString *remoteConfigURL; @@ -63,7 +66,7 @@ @interface SAConfigOptions () @property (nonatomic, assign) BOOL enableRemoteConfig; @property (nonatomic, assign) BOOL enableChannelMatch; @property (nonatomic, assign) BOOL enableDebugMode; -@property (nonatomic, assign) BOOL enableDeeplink; +@property (nonatomic, assign) BOOL enableDeepLink; @property (nonatomic, assign) BOOL enableAutoTrack; @@ -86,8 +89,6 @@ - (instancetype)initWithServerURL:(NSString *)serverURL launchOptions:(id)launch _minRequestHourInterval = 24; _maxRequestHourInterval = 48; - _loginIDKey = kSAIdentitiesLoginId; - #ifdef SENSORS_ANALYTICS_ENABLE_AUTOTRACK_CHILD_VIEWSCREEN _enableAutoTrackChildViewScreen = YES; #endif @@ -106,7 +107,7 @@ - (instancetype)initWithServerURL:(NSString *)serverURL launchOptions:(id)launch _enableRemoteConfig = YES; _enableChannelMatch = YES; _enableDebugMode = YES; - _enableDeeplink = YES; + _enableDeepLink = YES; _enableAutoTrack = YES; _storePlugins = [NSMutableArray array]; @@ -129,7 +130,6 @@ - (id)copyWithZone:(nullable NSZone *)zone { options.flushNetworkPolicy = self.flushNetworkPolicy; options.disableSDK = self.disableSDK; options.storePlugins = self.storePlugins; - options.loginIDKey = self.loginIDKey; options.enableSession = self.enableSession; options.disableDeviceId = self.disableDeviceId; @@ -171,8 +171,10 @@ - (id)copyWithZone:(nullable NSZone *)zone { options.enableRemoteConfig = self.enableRemoteConfig; options.enableChannelMatch = self.enableChannelMatch; options.enableDebugMode = self.enableDebugMode; - options.enableDeeplink = self.enableDeeplink; + options.enableDeepLink = self.enableDeepLink; options.enableAutoTrack = self.enableAutoTrack; + + options.customADChannelURL = self.customADChannelURL; #endif return options; diff --git a/SensorsAnalyticsSDK/Core/SAModuleManager.h b/SensorsAnalyticsSDK/Core/SAModuleManager.h index 825dc468..ac3a9460 100644 --- a/SensorsAnalyticsSDK/Core/SAModuleManager.h +++ b/SensorsAnalyticsSDK/Core/SAModuleManager.h @@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -@interface SAModuleManager (Deeplink) +@interface SAModuleManager (DeepLink) @end diff --git a/SensorsAnalyticsSDK/Core/SAModuleManager.m b/SensorsAnalyticsSDK/Core/SAModuleManager.m index c6881aba..af3fb499 100644 --- a/SensorsAnalyticsSDK/Core/SAModuleManager.m +++ b/SensorsAnalyticsSDK/Core/SAModuleManager.m @@ -37,7 +37,7 @@ static NSString * const kSAVisualizedModuleName = @"Visualized"; static NSString * const kSAEncryptModuleName = @"Encrypt"; -static NSString * const kSADeeplinkModuleName = @"Deeplink"; +static NSString * const kSADeepLinkModuleName = @"DeepLink"; static NSString * const kSANotificationModuleName = @"AppPush"; static NSString * const kSAAutoTrackModuleName = @"AutoTrack"; static NSString * const kSARemoteConfigModuleName = @"RemoteConfig"; @@ -124,7 +124,7 @@ - (void)loadModule:(NSString *)moduleName withConfigOptions:(SAConfigOptions *)c - (NSArray *)moduleNames { return @[kSAJavaScriptBridgeModuleName, kSANotificationModuleName, kSAChannelMatchModuleName, - kSADeeplinkModuleName, kSADebugModeModuleName, kSALocationModuleName, + kSADeepLinkModuleName, kSADebugModeModuleName, kSALocationModuleName, kSAAutoTrackModuleName, kSAVisualizedModuleName, kSAEncryptModuleName, kSADeviceOrientationModuleName, kSAExceptionModuleName, kSARemoteConfigModuleName]; } @@ -338,35 +338,27 @@ - (void)handleEncryptWithConfig:(nonnull NSDictionary *)encryptConfig { #pragma mark - -@implementation SAModuleManager (Deeplink) +@implementation SAModuleManager (DeepLink) -- (id)deeplinkManager { - id module = [self moduleWithName:kSADeeplinkModuleName]; - if ([module conformsToProtocol:@protocol(SADeeplinkModuleProtocol)] && [module conformsToProtocol:@protocol(SAModuleProtocol)]) { - id manager = module; +- (id)deepLinkManager { + id module = [self moduleWithName:kSADeepLinkModuleName]; + if ([module conformsToProtocol:@protocol(SADeepLinkModuleProtocol)] && [module conformsToProtocol:@protocol(SAModuleProtocol)]) { + id manager = module; return manager.isEnable ? manager : nil; } return nil; } -- (void)setLinkHandlerCallback:(void (^ _Nonnull)(NSString * _Nullable, BOOL, NSInteger))linkHandlerCallback { - [self.deeplinkManager setLinkHandlerCallback:linkHandlerCallback]; -} - - (NSDictionary *)latestUtmProperties { - return self.deeplinkManager.latestUtmProperties; + return self.deepLinkManager.latestUtmProperties; } - (NSDictionary *)utmProperties { - return self.deeplinkManager.utmProperties; + return self.deepLinkManager.utmProperties; } - (void)clearUtmProperties { - [self.deeplinkManager clearUtmProperties]; -} - -- (void)trackDeepLinkLaunchWithURL:(NSString *)url { - [self.deeplinkManager trackDeepLinkLaunchWithURL:url]; + [self.deepLinkManager clearUtmProperties]; } @end diff --git a/SensorsAnalyticsSDK/Core/SAModuleProtocol.h b/SensorsAnalyticsSDK/Core/SAModuleProtocol.h index 9f617908..cbc83ec1 100644 --- a/SensorsAnalyticsSDK/Core/SAModuleProtocol.h +++ b/SensorsAnalyticsSDK/Core/SAModuleProtocol.h @@ -113,14 +113,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -@protocol SADeeplinkModuleProtocol - -/// DeepLink 回调函数 -/// @param linkHandlerCallback callback 请求成功后的回调函数 -/// - params:创建渠道链接时填写的 App 内参数 -/// - succes:deeplink 唤起结果 -/// - appAwakePassedTime:获取渠道信息所用时间 -- (void)setLinkHandlerCallback:(void (^ _Nonnull)(NSString * _Nullable, BOOL, NSInteger))linkHandlerCallback; +@protocol SADeepLinkModuleProtocol /// 最新的来源渠道信息 @property (nonatomic, copy, nullable, readonly) NSDictionary *latestUtmProperties; @@ -131,10 +124,6 @@ NS_ASSUME_NONNULL_BEGIN /// 清除本次 DeepLink 解析到的 utm 信息 - (void)clearUtmProperties; -/// 触发 $AppDeepLinkLaunch 事件 -/// @param url 唤起 App 的 DeepLink url -- (void)trackDeepLinkLaunchWithURL:(NSString *)url; - @end #pragma mark - diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Private.h b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Private.h index ab497574..95eb4ee9 100644 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Private.h +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Private.h @@ -28,6 +28,7 @@ #import "SATrackEventObject.h" #import "SAAppLifecycle.h" #import "SASuperProperty.h" +#import "SAPresetProperty.h" @interface SensorsAnalyticsSDK(Private) @@ -60,6 +61,7 @@ @property (nonatomic, strong, readonly) SANetwork *network; @property (nonatomic, strong, readonly) SASuperProperty *superProperty; @property (nonatomic, strong, readonly) dispatch_queue_t serialQueue; +@property (nonatomic, strong, readonly) SAPresetProperty *presetProperty; @end diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h index cde1368b..d93e5746 100644 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h @@ -146,6 +146,16 @@ extern NSString * const SensorsAnalyticsIdentityKeyEmail; */ - (void)login:(NSString *)loginId withProperties:(NSDictionary * _Nullable )properties; +/** + ID-Mapping 3.0 登录,设置当前用户的 loginIDKey 和 loginId + + ⚠️ 此接口为 ID-Mapping 3.0 特殊场景下特定接口,请咨询确认后再使用 + + @param key 当前用户的登录 ID key + @param loginId 当前用户的登录 ID + */ +- (void)loginWithKey:(NSString *)key loginId:(NSString *)loginId; + /** * @abstract * 注销,清空当前用户的 loginId @@ -183,6 +193,14 @@ extern NSString * const SensorsAnalyticsIdentityKeyEmail; */ - (void)identify:(NSString *)anonymousId; +#pragma mark - 业务 ID + +/** + @abstract + ID-Mapping 3.0 功能下已绑定的业务 ID 列表 + */ +- (NSDictionary *)identities; + /** @abstract ID-Mapping 3.0 功能下绑定业务 ID 功能 diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h index 1d363fa4..b03a391f 100755 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h @@ -35,8 +35,8 @@ #import "SensorsAnalyticsSDK+DebugMode.h" #endif -#if __has_include("SensorsAnalyticsSDK+Deeplink.h") -#import "SensorsAnalyticsSDK+Deeplink.h" +#if __has_include("SensorsAnalyticsSDK+DeepLink.h") +#import "SensorsAnalyticsSDK+DeepLink.h" #endif #if __has_include("SensorsAnalyticsSDK+SAAutoTrack.h") diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m index a0b4bc6c..cdc2d9d9 100755 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m @@ -53,7 +53,7 @@ #import "SAUserDefaultsStorePlugin.h" #import "SASessionProperty.h" -#define VERSION @"4.2.6" +#define VERSION @"4.3.0" void *SensorsAnalyticsQueueTag = &SensorsAnalyticsQueueTag; @@ -208,10 +208,7 @@ - (instancetype)initWithConfigOptions:(nonnull SAConfigOptions *)configOptions { _eventTracker = [[SAEventTracker alloc] initWithQueue:_serialQueue]; _trackTimer = [[SATrackTimer alloc] init]; - _identifier = [[SAIdentifier alloc] initWithQueue:_readWriteQueue loginIDKey:_configOptions.loginIDKey]; - // SAIdentifier 中会校验 loginIDKey, 如果不合法会使用默认值 - // 此处更新 configOptions 中的 loginIDKey 和 SAIdentifier 中保持一致 - _configOptions.loginIDKey = _identifier.loginIDKey; + _identifier = [[SAIdentifier alloc] initWithQueue:_readWriteQueue]; _presetProperty = [[SAPresetProperty alloc] initWithQueue:_readWriteQueue]; @@ -389,13 +386,21 @@ - (void)login:(NSString *)loginId { } - (void)login:(NSString *)loginId withProperties:(NSDictionary * _Nullable )properties { + [self loginWithKey:kSAIdentitiesLoginId loginId:loginId properties:properties]; +} + +- (void)loginWithKey:(NSString *)key loginId:(NSString *)loginId { + [self loginWithKey:key loginId:loginId properties:nil]; +} + +- (void)loginWithKey:(NSString *)key loginId:(NSString *)loginId properties:(NSDictionary * _Nullable )properties { SASignUpEventObject *object = [[SASignUpEventObject alloc] initWithEventId:kSAEventNameSignUp]; object.dynamicSuperProperties = [self.superProperty acquireDynamicSuperProperties]; dispatch_async(self.serialQueue, ^{ - if (![self.identifier isValidLoginId:loginId]) { + if (![self.identifier isValidForLogin:key value:loginId]) { return; } - [self.identifier login:loginId]; + [self.identifier loginWithKey:key loginId:loginId]; [[NSNotificationCenter defaultCenter] postNotificationName:SA_TRACK_LOGIN_NOTIFICATION object:nil]; [self trackEventObject:object properties:properties]; }); @@ -687,28 +692,32 @@ - (void)profile:(NSString *)type properties:(NSDictionary *)properties { [self asyncTrackEventObject:object properties:properties]; } +- (NSDictionary *)identities { + return self.identifier.identities; +} + - (void)bind:(NSString *)key value:(NSString *)value { - if (![self.identifier isValidIdentity:key value:value]) { - return; - } + SABindEventObject *object = [[SABindEventObject alloc] initWithEventId:kSAEventNameBind]; + object.dynamicSuperProperties = [self.superProperty acquireDynamicSuperProperties]; dispatch_async(self.serialQueue, ^{ + if (![self.identifier isValidForBind:key value:value]) { + return; + } [self.identifier bindIdentity:key value:value]; + [self trackEventObject:object properties:nil]; }); - - SABindEventObject *object = [[SABindEventObject alloc] initWithEventId:kSAEventNameBind]; - [self asyncTrackEventObject:object properties:nil]; } - (void)unbind:(NSString *)key value:(NSString *)value { - if (![self.identifier isValidIdentity:key value:value]) { - return; - } + SAUnbindEventObject *object = [[SAUnbindEventObject alloc] initWithEventId:kSAEventNameUnbind]; + object.dynamicSuperProperties = [self.superProperty acquireDynamicSuperProperties]; dispatch_async(self.serialQueue, ^{ + if (![self.identifier isValidForUnbind:key value:value]) { + return; + } [self.identifier unbindIdentity:key value:value]; + [self trackEventObject:object properties:nil]; }); - - SAUnbindEventObject *object = [[SAUnbindEventObject alloc] initWithEventId:kSAEventNameUnbind]; - [self asyncTrackEventObject:object properties:nil]; } - (void)track:(NSString *)event { @@ -1278,30 +1287,50 @@ - (void)trackFromH5WithEventDict:(NSMutableDictionary *)eventDict isTrackEvent:( [[NSNotificationCenter defaultCenter] postNotificationName:SA_TRACK_LOGIN_NOTIFICATION object:nil]; }; - void(^loginBlock)(NSString *) = ^(NSString *newLoginId){ - if ([self.identifier isValidLoginId:newLoginId]) { - [self.identifier login:newLoginId]; - enqueueEvent[kSAEventLoginId] = newLoginId; + void(^loginBlock)(NSString *, NSString *) = ^(NSString *loginIDKey, NSString *newLoginId){ + if ([self.identifier isValidForLogin:loginIDKey value:newLoginId]) { + [self.identifier loginWithKey:loginIDKey loginId:newLoginId]; + // 传入的 newLoginId 为原始值,因此在这里做赋值时需要检查是否需要拼接 + if ([loginIDKey isEqualToString:kSAIdentitiesLoginId]) { + enqueueEvent[kSAEventLoginId] = newLoginId; + } else { + enqueueEvent[kSAEventLoginId] = [NSString stringWithFormat:@"%@%@%@", loginIDKey, kSALoginIdSpliceKey, newLoginId]; + } trackBlock(); } }; if([type isEqualToString:kSAEventTypeSignup]) { - if (identities) { - // 3.0 版本需要从 identities 中获取 login_id 信息 - NSString *newLoginId = identities[self.configOptions.loginIDKey]; - if (newLoginId) { - // 当可以从 identities 中获取到登录 ID 时正常处理登录逻辑 - loginBlock(newLoginId); - } else { - // 当 identities 中无法获取到登录 ID 时,只触发事件不进行 loginId 处理 - // 场景示例 :H5 和 App 端自定义 loginIDKey 不一致 - trackBlock(); - } - } else { + NSString *distinctId = eventDict[kSAEventDistinctId]; + + if (!identities) { // 2.0 版本逻辑,保持不变 - loginBlock(eventDict[kSAEventDistinctId]); + loginBlock(self.identifier.loginIDKey, distinctId); + return; + } + NSString *newLoginId = identities[self.identifier.loginIDKey]; + + NSMutableArray *array = [[distinctId componentsSeparatedByString:kSALoginIdSpliceKey] mutableCopy]; + NSString *key = array.firstObject; + // 移除 firstObject 的 loginIDKey,然后拼接后续的内容为 loginId + [array removeObjectAtIndex:0]; + NSString *value = [array componentsJoinedByString:kSALoginIdSpliceKey]; + NSSet *validKeys = [identities keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) { + return [obj isEqualToString:distinctId]; + }]; + if (newLoginId) { + loginBlock(self.identifier.loginIDKey, newLoginId); + } else if ([identities[key] isEqualToString:value]) { + // 当前 H5 的 distinct_id 是 key+value 拼接格式的,通过截取得到 loginIDKey 和 loginId + loginBlock(key, value); + } else if (validKeys.count == 1) { + // 当前 H5 的登录 ID 不是拼接格式的,则直接从 identities 中查找对应的 loginIDKey,只存在一个 key 时作为 loginIDKey + loginBlock(validKeys.anyObject, distinctId); + } else { + // 当 identities 中无法获取到登录 ID 时,只触发事件不进行 loginId 处理 + trackBlock(); } + } else { // 打通场景下,除登录事件外其他事件 enqueueEvent[kSAEventIdentities] = [self.identifier mergeH5Identities:identities eventType:type]; diff --git a/SensorsAnalyticsSDK/Core/Utils/SACommonUtility.h b/SensorsAnalyticsSDK/Core/Utils/SACommonUtility.h index 86540507..9e24b99e 100644 --- a/SensorsAnalyticsSDK/Core/Utils/SACommonUtility.h +++ b/SensorsAnalyticsSDK/Core/Utils/SACommonUtility.h @@ -36,4 +36,6 @@ /// 计算 hash + (NSString *)hashStringWithData:(NSData *)data; + + @end diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h new file mode 100644 index 00000000..746741fb --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h @@ -0,0 +1,72 @@ +// +// SADeepLinkConstants.h +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2021/12/10. +// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +#pragma mark - Event Name +extern NSString *const kSAAppDeepLinkLaunchEvent; +extern NSString *const kSADeepLinkMatchedResultEvent; +extern NSString *const kSADeferredDeepLinkJumpEvent; + +#pragma mark - Other +extern NSString *const kSADeepLinkLatestChannelsFileName; +extern NSString *const kSADeferredDeepLinkStatus; + +#pragma mark - Event Property +extern NSString *const kSAEventPropertyDeepLinkURL; +extern NSString *const kSAEventPropertyDeepLinkOptions; +extern NSString *const kSAEventPropertyDeepLinkFailReason; +extern NSString *const kSAEventPropertyDuration; +extern NSString *const kSAEventPropertyADMatchType; +extern NSString *const kSAEventPropertyADDeviceInfo; +extern NSString *const kSAEventPropertyADChannel; +extern NSString *const kSAEventPropertyADSLinkID; + +#pragma mark - Request Property +extern NSString *const kSARequestPropertyUserAgent; + +extern NSString *const kSARequestPropertyIDs; +extern NSString *const kSARequestPropertyUA; +extern NSString *const kSARequestPropertyOS; +extern NSString *const kSARequestPropertyOSVersion; +extern NSString *const kSARequestPropertyModel; +extern NSString *const kSARequestPropertyNetwork; +extern NSString *const kSARequestPropertyTimestamp; +extern NSString *const kSARequestPropertyAppID; +extern NSString *const kSARequestPropertyAppVersion; +extern NSString *const kSARequestPropertyAppParameter; +extern NSString *const kSARequestPropertyProject; + +#pragma mark - Response Property + +extern NSString *const kSAResponsePropertySLinkID; + +extern NSString *const kSAResponsePropertyCode; +extern NSString *const kSAResponsePropertyMessage; + +// DeepLink +extern NSString *const kSAResponsePropertyParams; +extern NSString *const kSAResponsePropertyChannelParams; + +// Deferred DeepLink +extern NSString *const kSAResponsePropertyParameter; +extern NSString *const kSAResponsePropertyADChannel; + +NSSet* sensorsdata_preset_channel_keys(void); diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m new file mode 100644 index 00000000..fd724aa5 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m @@ -0,0 +1,80 @@ +// +// SADeepLinkConstants.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2021/12/10. +// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import + +#pragma mark - Event Name + +NSString *const kSAAppDeepLinkLaunchEvent = @"$AppDeeplinkLaunch"; +NSString *const kSADeepLinkMatchedResultEvent = @"$AppDeeplinkMatchedResult"; +NSString *const kSADeferredDeepLinkJumpEvent = @"$AdAppDeferredDeepLinkJump"; + +#pragma mark - Other +NSString *const kSADeepLinkLatestChannelsFileName = @"latest_utms"; +NSString *const kSADeferredDeepLinkStatus = @"RequestDeferredDeepLinkStatus"; + +#pragma mark - Event Property +NSString *const kSAEventPropertyDeepLinkURL = @"$deeplink_url"; +NSString *const kSAEventPropertyDeepLinkOptions = @"$deeplink_options"; +NSString *const kSAEventPropertyDeepLinkFailReason = @"$deeplink_match_fail_reason"; +NSString *const kSAEventPropertyDuration = @"$event_duration"; +NSString *const kSAEventPropertyADMatchType = @"$ad_app_match_type"; +NSString *const kSAEventPropertyADDeviceInfo = @"$ad_device_info"; +NSString *const kSAEventPropertyADChannel= @"$ad_deeplink_channel_info"; +NSString *const kSAEventPropertyADSLinkID = @"$ad_slink_id"; + +#pragma mark - Request Property +NSString *const kSARequestPropertyUserAgent = @"$user_agent"; + +NSString *const kSARequestPropertyIDs = @"ids"; +NSString *const kSARequestPropertyUA = @"ua"; +NSString *const kSARequestPropertyOS = @"os"; +NSString *const kSARequestPropertyOSVersion = @"os_version"; +NSString *const kSARequestPropertyModel = @"model"; +NSString *const kSARequestPropertyNetwork = @"network"; +NSString *const kSARequestPropertyTimestamp = @"timestamp"; +NSString *const kSARequestPropertyAppID = @"app_id"; +NSString *const kSARequestPropertyAppVersion = @"app_version"; +NSString *const kSARequestPropertyAppParameter = @"app_parameter"; +NSString *const kSARequestPropertyProject = @"project"; + +#pragma mark - Response Property + +NSString *const kSAResponsePropertyCode = @"code"; +NSString *const kSAResponsePropertyMessage = @"msg"; + +NSString *const kSAResponsePropertySLinkID = @"ad_slink_id"; + +// DeepLink +NSString *const kSAResponsePropertyParams = @"page_params"; +NSString *const kSAResponsePropertyChannelParams = @"channel_params"; + +// Deferred DeepLink +NSString *const kSAResponsePropertyParameter = @"parameter"; +NSString *const kSAResponsePropertyADChannel = @"ad_channel"; + + +NSSet* sensorsdata_preset_channel_keys(void) { + return [NSSet setWithObjects:@"utm_campaign", @"utm_content", @"utm_medium", @"utm_source", @"utm_term", nil]; +} diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkEventProcessor.h b/SensorsAnalyticsSDK/Deeplink/SADeepLinkEventProcessor.h new file mode 100644 index 00000000..f997cdfa --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkEventProcessor.h @@ -0,0 +1,29 @@ +// +// SADeepLinkEventProcessor.h +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SADeepLinkProcessor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SADeepLinkEventProcessor : SADeepLinkProcessor + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkEventProcessor.m b/SensorsAnalyticsSDK/Deeplink/SADeepLinkEventProcessor.m new file mode 100644 index 00000000..b1f51edd --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkEventProcessor.m @@ -0,0 +1,33 @@ +// +// SADeepLinkEventProcessor.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import "SADeepLinkEventProcessor.h" + +@implementation SADeepLinkEventProcessor + +- (void)startWithProperties:(NSDictionary *)properties { + [self trackDeepLinkLaunch:properties]; +} + +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkProcessor.h b/SensorsAnalyticsSDK/Deeplink/SADeepLinkProcessor.h new file mode 100644 index 00000000..566479cb --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkProcessor.h @@ -0,0 +1,92 @@ +// +// SADeepLinkProcessor.h +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2021/12/13. +// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "SensorsAnalyticsSDK+DeepLink.h" +#import "SATrackEventObject.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef BOOL(^SADeepLinkCompletion)(SADeepLinkObject *object); + +@protocol SADeepLinkProcessorDelegate + +/** +@abstract +回传 DeepLink 解析到的渠道信息,并接收客户设置的 DeepLink Completion 函数 + +@param channels DeepLink 唤起时解析到的渠道信息,$utm_content 等内容 +@param latestChannels 最后一次 DeepLink 唤起时解析到的渠道信息,$latest_utm_content 等内容 +@param isDeferredDeepLink 是否为 Deferred DeepLink 场景,处理 completion 兼容场景 +*/ +- (SADeepLinkCompletion)sendChannels:(NSDictionary *_Nullable)channels latestChannels:(NSDictionary *_Nullable)latestChannels isDeferredDeepLink:(BOOL)isDeferredDeepLink; + +@end + +@interface SADeepLinkProcessor : NSObject + +/// 处理回调函数代理对象 +@property (nonatomic, weak) id delegate; + +@property (nonatomic, copy) NSURL *URL; + +@property (nonatomic, strong) NSSet *customChannelKeys; + +/// 子类重写,处理器支持的 URL 格式 ++ (BOOL)isValidURL:(NSURL *)url customChannelKeys:(NSSet *)customChannelKeys; + +/// 当前 Processor 是否可以通过短链 URL 唤起,默认不支持 +- (BOOL)canWakeUp; + +/// 开始处理 DeepLink 后续逻辑 +- (void)startWithProperties:(NSDictionary *_Nullable)properties; + +/// 触发 $AppDeeplinkLaunch 事件 +- (void)trackDeepLinkLaunch:(NSDictionary *)properties; + +/// 触发 $AppDeeplinkMatchedResult 事件 +- (void)trackDeepLinkMatchedResult:(NSDictionary *)properties; + +/// 获取渠道归因参数 +- (NSDictionary *)acquireChannels:(NSDictionary *)dictionary; + +/// 获取最后一次的渠道归因参数 +- (NSDictionary *)acquireLatestChannels:(NSDictionary *)dictionary; + +/// 设备信息 +- (NSString *)appInstallSource; + +@end + +@interface SADeepLinkProcessorFactory : NSObject + +/** + @abstract + 根据 URL 规则生成不同的 DeepLink Processor + + @param url 唤起的 URL + @param customChannelKeys 自定义渠道参数键值 + @return DeepLink 处理器 + */ ++ (SADeepLinkProcessor *)processorFromURL:(NSURL *_Nullable)url customChannelKeys:(NSSet *)customChannelKeys; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkProcessor.m b/SensorsAnalyticsSDK/Deeplink/SADeepLinkProcessor.m new file mode 100644 index 00000000..017853a2 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkProcessor.m @@ -0,0 +1,137 @@ +// +// SADeepLinkProcessor.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2021/12/13. +// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import "SADeepLinkProcessor.h" +#import "SADeepLinkConstants.h" +#import "SensorsAnalyticsSDK+Private.h" +#import "SAConstants+Private.h" +#import "SAIdentifier.h" +#import "SAQueryDeepLinkProcessor.h" +#import "SARequestDeepLinkProcessor.h" +#import "SADeepLinkEventProcessor.h" +#import "SADeferredDeepLinkProcessor.h" + +@interface SADeepLinkLaunchEventObject : SAPresetEventObject + +@end + +@implementation SADeepLinkLaunchEventObject + +// 手动调用接口采集 $AppDeeplinkLaunch 事件, 不需要添加 $latest_utm_xxx 属性 +- (void)addLatestUtmProperties:(NSDictionary *)properties { +} + +@end + +@implementation SADeepLinkProcessor + ++ (BOOL)isValidURL:(NSURL *)url customChannelKeys:(NSSet *)customChannelKeys { + return NO; +} + +- (BOOL)canWakeUp { + return NO; +} + +- (void)startWithProperties:(NSDictionary *)properties { + +} + +- (NSDictionary *)acquireChannels:(NSDictionary *)dictionary { + // SDK 预置属性,示例:$utm_content 和 用户自定义属性 + return [self presetKeyPrefix:@"$" customKeyPrefix:@"" dictionary:dictionary]; +} + +- (NSDictionary *)acquireLatestChannels:(NSDictionary *)dictionary { + // SDK 预置属性,示例:$latest_utm_content。 + // 用户自定义的属性,不是 SDK 的预置属性,因此以 _latest 开头,避免 SA 平台报错。示例:_lateset_customKey + return [self presetKeyPrefix:@"$latest_" customKeyPrefix:@"_latest_" dictionary:dictionary]; +} + +- (NSDictionary *)presetKeyPrefix:(NSString *)presetKeyPrefix customKeyPrefix:(NSString *)customKeyPrefix dictionary:(NSDictionary *)dictionary { + if (!presetKeyPrefix || !customKeyPrefix) { + return @{}; + } + + NSMutableDictionary *channels = [NSMutableDictionary dictionary]; + for (NSString *item in dictionary.allKeys) { + if ([sensorsdata_preset_channel_keys() containsObject:item]) { + NSString *key = [NSString stringWithFormat:@"%@%@", presetKeyPrefix, item]; + channels[key] = [dictionary[item] stringByRemovingPercentEncoding]; + } + if ([self.customChannelKeys containsObject:item]) { + NSString *key = [NSString stringWithFormat:@"%@%@", customKeyPrefix, item]; + channels[key] = [dictionary[item] stringByRemovingPercentEncoding]; + } + } + return channels; +} + +- (NSString *)appInstallSource { + NSMutableDictionary *sources = [NSMutableDictionary dictionary]; + sources[@"idfa"] = [SAIdentifier idfa]; + sources[@"idfv"] = [SAIdentifier idfv]; + NSMutableArray *result = [NSMutableArray array]; + for (NSString *key in sources.allKeys) { + [result addObject:[NSString stringWithFormat:@"%@=%@", key, sources[key]]]; + } + return [result componentsJoinedByString:@"##"]; +} + +- (void)trackDeepLinkLaunch:(NSDictionary *)properties { + SADeepLinkLaunchEventObject *object = [[SADeepLinkLaunchEventObject alloc] initWithEventId:kSAAppDeepLinkLaunchEvent]; + NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary]; + [eventProperties addEntriesFromDictionary:properties]; + eventProperties[kSAEventPropertyDeepLinkURL] = self.URL.absoluteString; + eventProperties[SA_EVENT_PROPERTY_APP_INSTALL_SOURCE] = [self appInstallSource]; + [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:eventProperties]; +} + +- (void)trackDeepLinkMatchedResult:(NSDictionary *)properties { + SAPresetEventObject *object = [[SAPresetEventObject alloc] initWithEventId:kSADeepLinkMatchedResultEvent]; + NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary]; + [eventProperties addEntriesFromDictionary:properties]; + eventProperties[kSAEventPropertyDeepLinkURL] = self.URL.absoluteString; + [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:eventProperties]; +} + +@end + +@implementation SADeepLinkProcessorFactory + ++ (SADeepLinkProcessor *)processorFromURL:(NSURL *)url customChannelKeys:(NSSet *)customChannelKeys { + SADeepLinkProcessor *object; + if ([SARequestDeepLinkProcessor isValidURL:url customChannelKeys:customChannelKeys]) { + object = [[SARequestDeepLinkProcessor alloc] init]; + } else if ([SAQueryDeepLinkProcessor isValidURL:url customChannelKeys:customChannelKeys]) { + object = [[SAQueryDeepLinkProcessor alloc] init];; + } else { + object = [[SADeepLinkProcessor alloc] init]; + } + object.URL = url; + object.customChannelKeys = customChannelKeys; + return object; +} + +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.h b/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.h index 7968a86d..cb76da15 100644 --- a/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.h +++ b/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.h @@ -1,5 +1,5 @@ // -// SADeeplinkManager.h +// SADeepLinkManager.h // SensorsAnalyticsSDK // // Created by 彭远洋 on 2020/1/6. @@ -18,31 +18,32 @@ // limitations under the License. // -#if ! __has_feature(objc_arc) -#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. -#endif - #import #import "SAConfigOptions.h" #import "SAModuleProtocol.h" +#import "SADeepLinkProcessor.h" NS_ASSUME_NONNULL_BEGIN -@interface SAConfigOptions (DeeplinkPrivate) +@interface SAConfigOptions (DeepLinkPrivate) -@property (nonatomic, assign) BOOL enableDeeplink; +@property (nonatomic, assign) BOOL enableDeepLink; @end -typedef void(^SALinkHandlerCallback)(NSString *_Nullable params, BOOL success, NSInteger appAwakePassedTime); - -@interface SADeeplinkManager : NSObject +@interface SADeepLinkManager : NSObject + (instancetype)defaultManager; @property (nonatomic, assign, getter=isEnable) BOOL enable; @property (nonatomic, strong) SAConfigOptions *configOptions; -@property (nonatomic, copy) SALinkHandlerCallback linkHandlerCallback; + +@property (nonatomic, copy) SADeepLinkCompletion oldCompletion; +@property (nonatomic, copy) SADeepLinkCompletion completion; + +- (void)trackDeepLinkLaunchWithURL:(NSString *)url; + +- (void)requestDeferredDeepLink:(NSDictionary *)properties; @end diff --git a/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m b/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m index 4494f3dc..6d133ecf 100644 --- a/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m +++ b/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m @@ -1,5 +1,5 @@ // -// SADeeplinkManager.m +// SADeepLinkManager.m // SensorsAnalyticsSDK // // Created by 彭远洋 on 2020/1/6. @@ -22,7 +22,7 @@ #error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. #endif -#import "SADeeplinkManager.h" +#import "SADeepLinkManager.h" #import "SensorsAnalyticsSDK+Private.h" #import "SAConstants+Private.h" #import "SAURLUtils.h" @@ -30,55 +30,42 @@ #import "SALog.h" #import "SAIdentifier.h" #import "SAJSONUtil.h" -#import "SensorsAnalyticsSDK+Deeplink.h" +#import "SANetwork.h" +#import "SAModuleManager.h" +#import "SAUserAgent.h" +#import "SensorsAnalyticsSDK+DeepLink.h" #import "SAApplication.h" - -static NSString *const kSAAppDeeplinkLaunchEvent = @"$AppDeeplinkLaunch"; -static NSString *const kSADeeplinkMatchedResultEvent = @"$AppDeeplinkMatchedResult"; -static NSString *const kSAEventPropertyDeepLinkURL = @"$deeplink_url"; - -static NSString *const kSavedDeepLinkInfoFileName = @"latest_utms"; - -@interface SADeepLinkLaunchEventObject : SAPresetEventObject - -- (instancetype)init; +#import "SADeepLinkConstants.h" +#import "SADeepLinkProcessor.h" +#import "SADeferredDeepLinkProcessor.h" +#import "SADeepLinkEventProcessor.h" + +@interface SADeepLinkManager () + +/// 本次唤起时的渠道信息 +@property (atomic, strong) NSMutableDictionary *channels; +/// 最后一次唤起时的渠道信息 +@property (atomic, copy) NSDictionary *latestChannels; +/// 自定义渠道字段名 +@property (nonatomic, copy) NSSet *customChannelKeys; +/// 本次冷启动时的 DeepLinkURL +@property (nonatomic, strong) NSURL *deepLinkURL; @end -@implementation SADeepLinkLaunchEventObject +@implementation SADeepLinkManager -- (instancetype)init { - self = [super initWithEventId:kSAAppDeeplinkLaunchEvent]; - return self; -} - -// 手动调用接口采集 $AppDeeplinkLaunch 事件, 不需要添加 $latest_utm_xxx 属性 -- (void)addLatestUtmProperties:(NSDictionary *)properties { -} - -@end - -@interface SADeeplinkManager () - -/// 包含 SDK 预置属性和用户自定义属性 -@property (atomic, strong) NSMutableDictionary *utms; -@property (atomic, copy) NSDictionary *latestUtms; -/// 预置属性列表 -@property (nonatomic, copy) NSSet *presetUtms; -/// 过滤后的用户自定义属性 -@property (nonatomic, copy) NSSet *sourceChannels; - -@property (nonatomic, strong) NSURL *deeplinkURL; - -@end - -@implementation SADeeplinkManager +typedef NS_ENUM(NSInteger, SADeferredDeepLinkStatus) { + SADeferredDeepLinkStatusInit = 0, + SADeferredDeepLinkStatusEnable, + SADeferredDeepLinkStatusDisable +}; + (instancetype)defaultManager { static dispatch_once_t onceToken; - static SADeeplinkManager *manager = nil; + static SADeepLinkManager *manager = nil; dispatch_once(&onceToken, ^{ - manager = [[SADeeplinkManager alloc] init]; + manager = [[SADeepLinkManager alloc] init]; }); return manager; } @@ -86,35 +73,54 @@ + (instancetype)defaultManager { - (instancetype)init { self = [super init]; if (self) { - // 设置需要解析的预置属性名 - _presetUtms = [NSSet setWithObjects:@"utm_campaign", @"utm_content", @"utm_medium", @"utm_source", @"utm_term", nil]; - _utms = [NSMutableDictionary dictionary]; + _channels = [NSMutableDictionary dictionary]; + NSInteger status = [self deferredDeepLinkStatus]; + BOOL isFirstDay = [SensorsAnalyticsSDK.sharedInstance.presetProperty isFirstDay]; + // isFirstDay 是为了避免用户版本升级场景下,不需要触发 Deferred DeepLink 逻辑的问题 + if (isFirstDay && status == SADeferredDeepLinkStatusInit) { + [self enableDeferredDeepLink]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appLifecycleStateDidChange:) name:kSAAppLifecycleStateDidChangeNotification object:nil]; + } else { + [self disableDeferredDeepLink]; + } } return self; } +- (void)appLifecycleStateDidChange:(NSNotification *)sender { + SAAppLifecycleState newState = [sender.userInfo[kSAAppLifecycleNewStateKey] integerValue]; + if (newState == SAAppLifecycleStateEnd) { + [self disableDeferredDeepLink]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:kSAAppLifecycleStateDidChangeNotification object:nil]; + } +} + +- (SADeferredDeepLinkStatus)deferredDeepLinkStatus { + return [[SAStoreManager sharedInstance] integerForKey:kSADeferredDeepLinkStatus]; +} + +- (void)enableDeferredDeepLink { + [[SAStoreManager sharedInstance] setInteger:SADeferredDeepLinkStatusEnable forKey:kSADeferredDeepLinkStatus]; +} + +- (void)disableDeferredDeepLink { + [[SAStoreManager sharedInstance] setInteger:SADeferredDeepLinkStatusDisable forKey:kSADeferredDeepLinkStatus]; +} + - (void)setConfigOptions:(SAConfigOptions *)configOptions { if ([SAApplication isAppExtension]) { - configOptions.enableDeeplink = NO; + configOptions.enableDeepLink = NO; } _configOptions = configOptions; - [self filterValidSourceChannnels:configOptions.sourceChannels]; - [self unarchiveSavedDeepLinkInfo:configOptions.enableSaveDeepLinkInfo]; + [self filterValidSourceChannelKeys:configOptions.sourceChannels]; + [self unarchiveLatestChannels:configOptions.enableSaveDeepLinkInfo]; [self handleLaunchOptions:configOptions.launchOptions]; [self acquireColdLaunchDeepLinkInfo]; - self.enable = configOptions.enableDeeplink; + self.enable = configOptions.enableDeepLink; } -- (void)setEnable:(BOOL)enable { - _enable = enable; - if (!enable) { - self.utms = nil; - self.latestUtms = nil; - } -} - -- (void)filterValidSourceChannnels:(NSArray *)sourceChannels { +- (void)filterValidSourceChannelKeys:(NSArray *)sourceChannels { NSSet *reservedPropertyName = sensorsdata_reserved_properties(); NSMutableSet *set = [[NSMutableSet alloc] init]; // 将用户自定义属性中与 SDK 保留字段相同的字段过滤掉 @@ -123,36 +129,43 @@ - (void)filterValidSourceChannnels:(NSArray *)sourceChannels { [set addObject:name]; } else { // 这里只做 LOG 提醒 - SALogError(@"deeplink source channel property [%@] is invalid!!!", name); + SALogError(@"deepLink source channel property [%@] is invalid!!!", name); } } - _sourceChannels = set; + self.customChannelKeys = set; } -- (void)unarchiveSavedDeepLinkInfo:(BOOL)enableSave { +- (void)unarchiveLatestChannels:(BOOL)enableSave { if (!enableSave) { - [[SAStoreManager sharedInstance] removeObjectForKey:kSavedDeepLinkInfoFileName]; + [[SAStoreManager sharedInstance] removeObjectForKey:kSADeepLinkLatestChannelsFileName]; return; } - NSDictionary *local = [[SAStoreManager sharedInstance] objectForKey:kSavedDeepLinkInfoFileName]; + NSDictionary *local = [[SAStoreManager sharedInstance] objectForKey:kSADeepLinkLatestChannelsFileName]; if (!local) { return; } + NSArray *array = @[@{@"names":sensorsdata_preset_channel_keys(), @"prefix":@"$latest"}, + @{@"names":self.customChannelKeys, @"prefix":@"_latest"}]; NSMutableDictionary *latest = [NSMutableDictionary dictionary]; - for (NSString *name in _presetUtms) { - NSString *newName = [NSString stringWithFormat:@"$latest_%@", name]; - if (local[newName]) { - latest[newName] = local[newName]; + for (NSDictionary *obj in array) { + for (NSString *name in obj[@"names"]) { + // 升级版本时 sourceChannels 可能会发生变化,过滤掉本次 sourceChannels 中已不包含的字段 + NSString *latestKey = [NSString stringWithFormat:@"%@_%@", obj[@"prefix"], name]; + NSString *value = [local[latestKey] stringByRemovingPercentEncoding]; + if (value.length > 0) { + latest[latestKey] = value; + } } } - // 升级版本时 sourceChannels 可能会发生变化,过滤掉本次 sourceChannels 中已不包含的字段 - for (NSString *name in _sourceChannels) { - NSString *newName = [NSString stringWithFormat:@"_latest_%@", name]; - if (local[newName]) { - latest[newName] = local[newName]; - } + self.latestChannels = latest; +} + +/// 开启本地保存 DeepLinkInfo 开关时,每次 DeepLink 唤起解析后都需要更新本地文件中数据 +- (void)archiveLatestChannels:(NSDictionary *)dictionary { + if (!_configOptions.enableSaveDeepLinkInfo) { + return; } - self.latestUtms = latest; + [[SAStoreManager sharedInstance] setObject:dictionary forKey:kSADeepLinkLatestChannelsFileName]; } // 记录冷启动的 DeepLink URL @@ -164,7 +177,7 @@ - (void)handleLaunchOptions:(id)options { UISceneConnectionOptions *sceneOptions = (UISceneConnectionOptions *)options; NSUserActivity *userActivity = sceneOptions.userActivities.allObjects.firstObject; UIOpenURLContext *urlContext = sceneOptions.URLContexts.allObjects.firstObject; - _deeplinkURL = urlContext.URL ? urlContext.URL : userActivity.webpageURL; + _deepLinkURL = urlContext.URL ? urlContext.URL : userActivity.webpageURL; return; } } @@ -175,7 +188,7 @@ - (void)handleLaunchOptions:(id)options { NSDictionary *launchOptions = (NSDictionary *)options; if ([launchOptions.allKeys containsObject:UIApplicationLaunchOptionsURLKey]) { //通过 SchemeLink 唤起 App - _deeplinkURL = launchOptions[UIApplicationLaunchOptionsURLKey]; + _deepLinkURL = launchOptions[UIApplicationLaunchOptionsURLKey]; } #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 else if (@available(iOS 8.0, *)) { @@ -184,7 +197,7 @@ - (void)handleLaunchOptions:(id)options { if ([type isEqualToString:NSUserActivityTypeBrowsingWeb]) { //通过 UniversalLink 唤起 App NSUserActivity *userActivity = userActivityDictionary[@"UIApplicationLaunchOptionsUserActivityKey"]; - _deeplinkURL = userActivity.webpageURL; + _deepLinkURL = userActivity.webpageURL; } } #endif @@ -196,22 +209,24 @@ - (void)acquireColdLaunchDeepLinkInfo { // 避免方法被多次调用 static dispatch_once_t deepLinkToken; dispatch_once(&deepLinkToken, ^{ - if (![self canHandleURL:_deeplinkURL]) { + if (![self canHandleURL:_deepLinkURL]) { return; } - [self checkDeepLinkMode:_deeplinkURL]; + + [self disableDeferredDeepLink]; + [self handleDeepLinkURL:_deepLinkURL]; }); } -#pragma mark - utm properties +#pragma mark - channel properties /// $latest_utm_* 属性,当本次启动是通过 DeepLink 唤起时所有 event 事件都会新增这些属性 - (nullable NSDictionary *)latestUtmProperties { - return [self.latestUtms copy]; + return [self.latestChannels copy]; } /// $utm_* 属性,当通过 DeepLink 唤起 App 时 只针对 $AppStart 事件和第一个 $AppViewScreen 事件会新增这些属性 - (NSDictionary *)utmProperties { - return [self.utms copy]; + return [self.channels copy]; } /// 在固定场景下需要清除 utm_* 属性 @@ -219,236 +234,101 @@ - (NSDictionary *)utmProperties { // 通过 DeepLink 唤起 App 并触发第一个页面浏览时需要清除本次的 utm 属性 // 退出 App 时需要清除本次的 utms 属性 - (void)clearUtmProperties { - [self.utms removeAllObjects]; + [self.channels removeAllObjects]; } /// 只有通过 DeepLink 唤起 App 时需要清除 latest utms - (void)clearLatestUtmProperties { - self.latestUtms = nil; + self.latestChannels = nil; } /// 清空上一次 DeepLink 唤起时的信息,并保存本次唤起的 URL -- (void)clearLastDeepLinkInfo:(NSURL *)url { +- (void)clearLastDeepLinkInfo { [self clearUtmProperties]; [self clearLatestUtmProperties]; // 删除本地保存的 DeepLink 信息 - [self saveDeepLinkInfo:nil]; - self.utms[kSAEventPropertyDeepLinkURL] = url.absoluteString; -} - -#pragma mark - save latest utms in local file -/// 开启本地保存 DeepLinkInfo 开关时,每次 DeepLink 唤起解析后都需要更新本地文件中数据 -- (void)saveDeepLinkInfo:(NSDictionary *)dictionary { - if (!_configOptions.enableSaveDeepLinkInfo) { - return; - } - [[SAStoreManager sharedInstance] setObject:dictionary forKey:kSavedDeepLinkInfoFileName]; + [self archiveLatestChannels:nil]; } -#pragma mark - parse utms +#pragma mark - Handle DeepLink - (BOOL)canHandleURL:(NSURL *)url { if (![url isKindOfClass:NSURL.class]) { return NO; } - return [self isValidURLForLocalMode:url] || [self isValidURLForServerMode:url]; -} - -// URL 的 Query 中包含一个或多个 utm_* 参数。示例:https://sensorsdata.cn?utm_content=1&utm_campaign=2 -// utm_* 参数共五个,"utm_campaign", "utm_content", "utm_medium", "utm_source", "utm_term" -- (BOOL)isValidURLForLocalMode:(NSURL *)url { - NSDictionary *queryItems = [SAURLUtils queryItemsWithURL:url]; - for (NSString *key in _presetUtms) { - if (queryItems[key]) { - return YES; - } - } - for (NSString *key in _sourceChannels) { - if (queryItems[key]) { - return YES; - } - } - return NO; + SADeepLinkProcessor *processor = [SADeepLinkProcessorFactory processorFromURL:url customChannelKeys:self.customChannelKeys]; + return processor.canWakeUp; } - (BOOL)handleURL:(NSURL *)url { // 当 url 和 _deepLinkURL 相同时,则表示本次触发是冷启动触发,已通过 acquireColdLaunchDeepLinkInfo 方法处理,这里不需要重复处理 - NSString *absoluteString = _deeplinkURL.absoluteString; - _deeplinkURL = nil; + NSString *absoluteString = _deepLinkURL.absoluteString; + _deepLinkURL = nil; if ([url.absoluteString isEqualToString:absoluteString]) { return NO; } - return [self checkDeepLinkMode:url]; + return [self handleDeepLinkURL:url]; } -- (BOOL)checkDeepLinkMode:(NSURL *)url { - if (![url isKindOfClass:NSURL.class]) { +- (BOOL)handleDeepLinkURL:(NSURL *)url { + if (!url) { return NO; } - [self clearLastDeepLinkInfo:url]; - - if ([self isValidURLForServerMode:url]) { - // ServerMode 先触发 Launch 事件再请求接口,Launch 事件中只新增 $deeplink_url 属性 - SADeepLinkLaunchEventObject *object = [[SADeepLinkLaunchEventObject alloc] init]; - NSMutableDictionary *properties = [NSMutableDictionary dictionary]; - properties[SA_EVENT_PROPERTY_APP_INSTALL_SOURCE] = [self appInstallSource]; - [properties addEntriesFromDictionary:self.utms]; - [properties addEntriesFromDictionary:self.latestUtms]; - properties[kSAEventPropertyDeepLinkURL] = url.absoluteString; - [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:properties]; - [self requestDeepLinkInfo:url]; - } else { - // LocalMode 先解析 Query 参数后再触发 Launch 事件,Launch 事件中有 utm_* 属性信息 - NSDictionary *dictionary = [SAURLUtils queryItemsWithURL:url]; - [self acquireCurrentDeepLinkInfo:dictionary]; - SADeepLinkLaunchEventObject *object = [[SADeepLinkLaunchEventObject alloc] init]; - NSMutableDictionary *properties = [NSMutableDictionary dictionary]; - [properties addEntriesFromDictionary:self.utms]; - [properties addEntriesFromDictionary:self.latestUtms]; - properties[kSAEventPropertyDeepLinkURL] = url.absoluteString; - [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:properties]; - } - return YES; -} -- (NSString *)appInstallSource { - NSMutableDictionary *sources = [NSMutableDictionary dictionary]; - sources[@"idfa"] = [SAIdentifier idfa]; - sources[@"idfv"] = [SAIdentifier idfv]; - NSMutableArray *result = [NSMutableArray array]; - [sources enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { - [result addObject:[NSString stringWithFormat:@"%@=%@", key, obj]]; - }]; - return result.count > 0 ? [result componentsJoinedByString:@"##"] : @""; + [self clearLastDeepLinkInfo]; + + // 在 channels 中保存本次唤起的 DeepLink URL 添加到指定事件中 + self.channels[kSAEventPropertyDeepLinkURL] = url.absoluteString; + + SADeepLinkProcessor *processor = [SADeepLinkProcessorFactory processorFromURL:url customChannelKeys:self.customChannelKeys]; + processor.delegate = self; + [processor startWithProperties:nil]; + return processor.canWakeUp; } -/// 通过 URL 的 Query 获取本次的 utm_* 属性 -- (void)acquireCurrentDeepLinkInfo:(NSDictionary *)dictionary { - if (![dictionary isKindOfClass:NSDictionary.class]) { +#pragma mark - Public Methods +- (void)trackDeepLinkLaunchWithURL:(NSString *)url { + if (url && ![url isKindOfClass:NSString.class]) { + SALogError(@"deeplink url must be NSString. got: %@ %@", url.class, url); return; } - self.utms = [self acquireUtmProperties:dictionary]; - self.latestUtms = [self acquireLatestUtmProperties:dictionary]; + SADeepLinkEventProcessor *processor = [[SADeepLinkEventProcessor alloc] init]; + [processor startWithProperties:nil]; } -- (NSMutableDictionary *)acquireUtmProperties:(NSDictionary *)dictionary { - NSMutableDictionary *utmProperties = [NSMutableDictionary dictionary]; - for (NSString *propKey in _presetUtms) { - NSString *propValue = [dictionary[propKey] stringByRemovingPercentEncoding]; - if (propValue.length > 0) { - NSString *utmKey = [NSString stringWithFormat:@"$%@", propKey]; - utmProperties[utmKey] = propValue; - } - } - for (NSString *propKey in _sourceChannels) { - NSString *propValue = [dictionary[propKey] stringByRemovingPercentEncoding]; - if (propValue.length > 0) { - utmProperties[propKey] = propValue; - } +- (void)requestDeferredDeepLink:(NSDictionary *)properties { + // 当不是首次安装 App 时,则不需要再触发 Deferred DeepLink 请求 + if ([self deferredDeepLinkStatus] == SADeferredDeepLinkStatusDisable) { + return; } - return utmProperties; -} -- (NSDictionary *)acquireLatestUtmProperties:(NSDictionary *)dictionary { - __block NSMutableDictionary *latest = [NSMutableDictionary dictionary]; - void(^block)(NSString *propKey, NSString *keyPrefix) = ^(NSString *propKey, NSString *keyPrefix) { - NSString *propValue = [dictionary[propKey] stringByRemovingPercentEncoding]; - if (propValue.length > 0) { - NSString *latestKey = [NSString stringWithFormat:@"%@_%@", keyPrefix, propKey]; - latest[latestKey] = propValue; - } - }; - //SDK 预置属性,示例:$latest_utm_content。 - for (NSString *propKey in _presetUtms) { - block(propKey, @"$latest"); - } - // 用户自定义的属性,不是 SDK 的预置属性,因此以 _latest 开头,避免 SA 平台报错。示例:_lateset_customKey - for (NSString *propKey in _sourceChannels) { - block(propKey, @"_latest"); - } - [self saveDeepLinkInfo:latest]; - return latest; -} + [self disableDeferredDeepLink]; -#pragma mark - Server Mode -/// URL 的 Path 符合特定规则。示例:https://{域名}/sd/{appId}/{key} 或 {scheme}://sensorsdata/sd/{key} -- (BOOL)isValidURLForServerMode:(NSURL *)url { - NSArray *pathComponents = url.pathComponents; - if (pathComponents.count < 2 || ![pathComponents[1] isEqualToString:@"sd"]) { - return NO; - } - NSString *host = SensorsAnalyticsSDK.sharedInstance.network.serverURL.host; - return ([url.host isEqualToString:@"sensorsdata"] || [url.host isEqualToString:host]); + SADeferredDeepLinkProcessor *processor = [[SADeferredDeepLinkProcessor alloc] init]; + processor.delegate = self; + processor.customChannelKeys = self.customChannelKeys; + [processor startWithProperties:properties]; } -- (NSURLRequest *)buildRequestWithURL:(NSURL *)url { - NSURLComponents *components = SensorsAnalyticsSDK.sharedInstance.network.baseURLComponents; - if (!components) { - return nil; - } - components.path = [components.path stringByAppendingPathComponent:@"/sdk/deeplink/param"]; - NSString *key = url.lastPathComponent; - NSString *project = SensorsAnalyticsSDK.sharedInstance.network.project; - components.query = [NSString stringWithFormat:@"key=%@&project=%@&system_type=IOS", key, project]; - NSURL *URL = [components URL]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; - request.timeoutInterval = 60; - [request setHTTPMethod:@"GET"]; - return request; -} +#pragma mark - processor delegate +- (SADeepLinkCompletion)sendChannels:(NSDictionary *)channels latestChannels:(NSDictionary *)latestChannels isDeferredDeepLink:(BOOL)isDeferredDeepLink { + // 合并本次唤起的渠道信息,channels 中已保存 DeepLink URL,所以不能直接覆盖 + [self.channels addEntriesFromDictionary:channels]; -- (void)requestDeepLinkInfo:(NSURL *)url { - NSURLRequest *request = [self buildRequestWithURL:url]; - if (!request) { - return; + // 覆盖本次唤起的渠道信息,只包含 $latest_utm_* 和 _latest_* 属性 + self.latestChannels = latestChannels; + [self archiveLatestChannels:latestChannels]; + + if (self.completion) { + return self.completion; } - NSTimeInterval start = NSDate.date.timeIntervalSince1970; - NSURLSessionDataTask *task = [SAHTTPSession.sharedInstance dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data, NSHTTPURLResponse *_Nullable response, NSError *_Nullable error) { - NSTimeInterval interval = (NSDate.date.timeIntervalSince1970 - start); - NSDictionary *result; - NSString *errorMsg; - BOOL success = NO; - if (response.statusCode == 200 && data) { - result = [SAJSONUtil JSONObjectWithData:data]; - errorMsg = result[@"errorMsg"]; - success = errorMsg.length <= 0; - self.latestUtms = [self acquireLatestUtmProperties:result[@"channel_params"]]; - } else { - NSString *codeMsg = [NSString stringWithFormat:@"http status code: %@",@(response.statusCode)]; - errorMsg = error.localizedDescription ?: codeMsg; - } - [self trackDeeplinkMatchedResult:url result:result interval:interval errorMsg:errorMsg]; - if (self.linkHandlerCallback) { - self.linkHandlerCallback(result[@"page_params"], success, interval * 1000); - } - }]; - [task resume]; -} -#pragma mark - deeplink event -/// 对外接口, 用于客户手动调用采集 $AppDeeplinkLaunch 事件 -/// @param url $deeplink_url -- (void)trackDeepLinkLaunchWithURL:(NSString *)url { - if (url && ![url isKindOfClass:NSString.class]) { - SALogError(@"deeplink url must be NSString. got: %@ %@", url.class, url); - return; + // 1. 当是 DeferredDeepLink 时,不兼容老版本 completion,不做回调处理 + // 2. 当老版本 completion 也不存在时,不做回调处理 + if (isDeferredDeepLink || !self.oldCompletion) { + return nil; } - SADeepLinkLaunchEventObject *object = [[SADeepLinkLaunchEventObject alloc] init]; - NSMutableDictionary *properties = [NSMutableDictionary dictionary]; - properties[kSAEventPropertyDeepLinkURL] = url; - properties[SA_EVENT_PROPERTY_APP_INSTALL_SOURCE] = [self appInstallSource]; - [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:properties]; -} -- (void)trackDeeplinkMatchedResult:(NSURL *)url result:(NSDictionary *)result interval:(NSTimeInterval)interval errorMsg:(NSString *)errorMsg { - NSMutableDictionary *props = [NSMutableDictionary dictionary]; - props[@"$event_duration"] = [NSString stringWithFormat:@"%.3f", interval]; - props[@"$deeplink_options"] = result[@"page_params"]; - props[@"$deeplink_match_fail_reason"] = errorMsg.length ? errorMsg : nil; - props[kSAEventPropertyDeepLinkURL] = url.absoluteString; - NSDictionary *utms = [self acquireUtmProperties:result[@"channel_params"]]; - [props addEntriesFromDictionary:utms]; - SAPresetEventObject *object = [[SAPresetEventObject alloc] initWithEventId:kSADeeplinkMatchedResultEvent]; - [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:props]; + return self.oldCompletion; } @end diff --git a/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.h b/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.h new file mode 100644 index 00000000..0e8b6fa7 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.h @@ -0,0 +1,29 @@ +// +// SADeferredDeepLinkProcessor.h +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SADeepLinkProcessor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SADeferredDeepLinkProcessor : SADeepLinkProcessor + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m b/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m new file mode 100644 index 00000000..8a11ee40 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m @@ -0,0 +1,163 @@ +// +// SADeferredDeepLinkProcessor.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import "SADeferredDeepLinkProcessor.h" +#import "SADeepLinkConstants.h" +#import "SensorsAnalyticsSDK+Private.h" +#import "SAJSONUtil.h" +#import "SANetwork.h" +#import "SAUserAgent.h" + +@implementation SADeferredDeepLinkProcessor + +- (void)startWithProperties:(NSDictionary *)properties { + NSString *userAgent = properties[kSARequestPropertyUserAgent]; + if (userAgent.length > 0) { + NSMutableDictionary *newProperties = [NSMutableDictionary dictionaryWithDictionary:properties]; + [newProperties removeObjectForKey:kSARequestPropertyUserAgent]; + NSURLRequest *request = [self buildRequest:userAgent properties:newProperties]; + [self requestForDeferredDeepLink:request]; + } else { + __block typeof(self) weakSelf = self; + [SAUserAgent loadUserAgentWithCompletion:^(NSString *userAgent) { + NSURLRequest *request = [weakSelf buildRequest:userAgent properties:properties]; + [weakSelf requestForDeferredDeepLink:request]; + }]; + } +} + +- (void)requestForDeferredDeepLink:(NSURLRequest *)request { + if (!request) { + return; + } + NSTimeInterval start = NSDate.date.timeIntervalSince1970; + NSURLSessionDataTask *task = [SAHTTPSession.sharedInstance dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data, NSHTTPURLResponse *_Nullable response, NSError *_Nullable error) { + NSTimeInterval interval = (NSDate.date.timeIntervalSince1970 - start); + + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + properties[kSAEventPropertyDuration] = [NSString stringWithFormat:@"%.3f", interval]; + properties[kSAEventPropertyADMatchType] = @"deferred deeplink"; + + NSData *deviceInfoData = [[self appInstallSource] dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64 = [deviceInfoData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; + properties[kSAEventPropertyADDeviceInfo] = base64; + + SADeepLinkObject *obj = [[SADeepLinkObject alloc] init]; + obj.appAwakePassedTime = interval * 1000; + obj.success = NO; + + NSDictionary *latestChannels; + NSString *slinkID; + + if (response.statusCode == 200) { + NSDictionary *result = [SAJSONUtil JSONObjectWithData:data]; + properties[kSAEventPropertyDeepLinkOptions] = result[kSAResponsePropertyParameter]; + properties[kSAEventPropertyADChannel] = result[kSAResponsePropertyADChannel]; + properties[kSAEventPropertyDeepLinkFailReason] = result ? result[kSAResponsePropertyMessage] : @"response is null"; + properties[kSAEventPropertyADSLinkID] = result[kSAResponsePropertySLinkID]; + obj.params = result[kSAResponsePropertyParameter]; + obj.adChannels = result[kSAResponsePropertyADChannel]; + obj.success = (result[kSAResponsePropertyCode] && [result[kSAResponsePropertyCode] integerValue] == 0); + + // Result 事件中添加 $utm_* 属性 + [properties addEntriesFromDictionary:[self acquireChannels:result[kSAResponsePropertyChannelParams]]]; + + // 解析并并转换为 $latest_utm_content 属性,并添加到后续事件所有事件中 + latestChannels = [self acquireLatestChannels:result[kSAResponsePropertyChannelParams]]; + slinkID = result[kSAResponsePropertySLinkID]; + } else { + NSString *codeMsg = [NSString stringWithFormat:@"http status code: %@",@(response.statusCode)]; + properties[kSAEventPropertyDeepLinkFailReason] = error.localizedDescription ?: codeMsg; + } + + // 确保调用客户设置的 completion 是在主线程中 + dispatch_async(dispatch_get_main_queue(), ^{ + SADeepLinkCompletion completion; + if ([self.delegate respondsToSelector:@selector(sendChannels:latestChannels:isDeferredDeepLink:)]) { + // 当前方式不需要获取 channels 信息,只需要保存 latestChannels 信息 + completion = [self.delegate sendChannels:nil latestChannels:latestChannels isDeferredDeepLink:YES]; + } + if (obj.success && !completion) { + properties[kSAEventPropertyDeepLinkFailReason] = @"未调用 setDeepLinkCompletion 方法设置回调函数"; + } + [self trackDeepLinkMatchedResult:properties]; + + if (!completion) { + return; + } + + BOOL jumpSuccess = completion(obj); + // 只有当请求成功,并且客户跳转页面成功后,触发 $AdAppDeferredDeepLinkJump 事件 + if (!obj.success || !jumpSuccess) { + return; + } + SAPresetEventObject *object = [[SAPresetEventObject alloc] initWithEventId:kSADeferredDeepLinkJumpEvent]; + NSMutableDictionary *jumpProps = [NSMutableDictionary dictionary]; + jumpProps[kSAEventPropertyDeepLinkOptions] = obj.params; + jumpProps[kSAEventPropertyADSLinkID] = slinkID; + [SensorsAnalyticsSDK.sharedInstance asyncTrackEventObject:object properties:jumpProps]; + }); + }]; + [task resume]; +} + +- (NSURLRequest *)buildRequest:(NSString *)userAgent properties:(NSDictionary *)properties { + NSString *channelURL = SensorsAnalyticsSDK.sharedInstance.configOptions.customADChannelURL; + NSURLComponents *components; + if (channelURL.length > 0) { + components = [[NSURLComponents alloc] initWithString:channelURL]; + } else { + components = SensorsAnalyticsSDK.sharedInstance.network.baseURLComponents; + } + + if (!components) { + return nil; + } + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + NSData *data = [[self appInstallSource] dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64 = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; + params[kSARequestPropertyIDs] = base64; + params[kSARequestPropertyUA] = userAgent; + params[kSARequestPropertyOS] = @"iOS"; + params[kSARequestPropertyOSVersion] = UIDevice.currentDevice.systemVersion; + params[kSARequestPropertyModel] = UIDevice.currentDevice.model; + params[kSARequestPropertyNetwork] = [SANetwork networkTypeString]; + NSInteger timestamp = [@([[NSDate date] timeIntervalSince1970] * 1000) integerValue]; + params[kSARequestPropertyTimestamp] = @(timestamp); + params[kSARequestPropertyAppID] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; + params[kSARequestPropertyAppVersion] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + params[kSARequestPropertyAppParameter] = [SAJSONUtil stringWithJSONObject:properties]; + params[kSARequestPropertyProject] = SensorsAnalyticsSDK.sharedInstance.network.project; + components.path = [components.path stringByAppendingPathComponent:@"/slink/ddeeplink"]; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[components URL]]; + request.timeoutInterval = 60; + request.HTTPBody = [SAJSONUtil dataWithJSONObject:params]; + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + return request; +} + +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SAQueryDeepLinkProcessor.h b/SensorsAnalyticsSDK/Deeplink/SAQueryDeepLinkProcessor.h new file mode 100644 index 00000000..dd07cd22 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SAQueryDeepLinkProcessor.h @@ -0,0 +1,29 @@ +// +// SAQueryDeepLinkProcessor.h +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SADeepLinkProcessor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SAQueryDeepLinkProcessor : SADeepLinkProcessor + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SAQueryDeepLinkProcessor.m b/SensorsAnalyticsSDK/Deeplink/SAQueryDeepLinkProcessor.m new file mode 100644 index 00000000..8e2621e6 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SAQueryDeepLinkProcessor.m @@ -0,0 +1,70 @@ +// +// SAQueryDeepLinkProcessor.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import "SAQueryDeepLinkProcessor.h" +#import "SADeepLinkConstants.h" +#import "SensorsAnalyticsSDK+Private.h" +#import "SensorsAnalyticsSDK+DeepLink.h" +#import "SAConstants+Private.h" +#import "SAURLUtils.h" +#import "SAIdentifier.h" +#import "SAJSONUtil.h" +#import "SANetwork.h" +#import "SAUserAgent.h" + +@implementation SAQueryDeepLinkProcessor + +// URL 的 Query 中包含一个或多个 utm_* 参数。示例:https://sensorsdata.cn?utm_content=1&utm_campaign=2 +// utm_* 参数共五个,"utm_campaign", "utm_content", "utm_medium", "utm_source", "utm_term" ++ (BOOL)isValidURL:(NSURL *)url customChannelKeys:(NSSet *)customChannelKeys { + NSMutableSet *sets = [NSMutableSet setWithSet:customChannelKeys]; + [sets unionSet:sensorsdata_preset_channel_keys()]; + NSDictionary *queryItems = [SAURLUtils queryItemsWithURL:url]; + for (NSString *key in sets) { + if (queryItems[key]) { + return YES; + } + } + return NO; +} + +- (BOOL)canWakeUp { + return YES; +} + +- (void)startWithProperties:(NSDictionary *)properties { + NSDictionary *queryItems = [SAURLUtils queryItemsWithURL:self.URL]; + NSDictionary *channels = [self acquireChannels:queryItems]; + NSDictionary *latestChannels = [self acquireLatestChannels:queryItems]; + NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary]; + [eventProperties addEntriesFromDictionary:properties]; + [eventProperties addEntriesFromDictionary:channels]; + [eventProperties addEntriesFromDictionary:latestChannels]; + [self trackDeepLinkLaunch:eventProperties]; + + if ([self.delegate respondsToSelector:@selector(sendChannels:latestChannels:isDeferredDeepLink:)]) { + [self.delegate sendChannels:channels latestChannels:latestChannels isDeferredDeepLink:NO]; + } +} +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.h b/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.h new file mode 100644 index 00000000..3d604314 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.h @@ -0,0 +1,29 @@ +// +// SARequestDeepLinkProcessor.h +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SADeepLinkProcessor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SARequestDeepLinkProcessor : SADeepLinkProcessor + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m b/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m new file mode 100644 index 00000000..77e8f11b --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m @@ -0,0 +1,159 @@ +// +// SARequestDeepLinkProcessor.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2022/3/14. +// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import "SARequestDeepLinkProcessor.h" +#import "SADeepLinkConstants.h" +#import "SensorsAnalyticsSDK+Private.h" +#import "SAJSONUtil.h" + +static NSString *const kSchemeDeepLinkHost = @"sensorsdata"; + +@implementation SARequestDeepLinkProcessor + ++ (BOOL)isValidURL:(NSURL *)url customChannelKeys:(NSSet *)customChannelKeys { + if ([self isCustomDeepLinkURL:url]) { + return YES; + } + return [self isNormalDeepLinkURL:url]; +} + +/// URL 的 Path 符合特定规则。示例:https://{域名}/sd/{appId}/{key} 或 {scheme}://sensorsdata/sd/{key} +/// 数据接收地址环境对应的域名短链 ++ (BOOL)isNormalDeepLinkURL:(NSURL *)url { + NSArray *pathComponents = url.pathComponents; + if (pathComponents.count < 2 || ![pathComponents[1] isEqualToString:@"sd"]) { + return NO; + } + NSString *host = SensorsAnalyticsSDK.sharedInstance.network.serverURL.host; + return ([url.host isEqualToString:kSchemeDeepLinkHost] || [url.host isEqualToString:host]); +} + +/// URL 的 Path 符合特定规则。示例:https://{ 自定义域名}/slink/{appId}/{key} 或 {scheme}://sensorsdata/slink/{key} +/// 自定义域名对应的域名短链 ++ (BOOL)isCustomDeepLinkURL:(NSURL *)url { + // 如果没有配置 SaaS 环境域名,则不处理 + NSString *channelURL = SensorsAnalyticsSDK.sharedInstance.configOptions.customADChannelURL; + if (channelURL.length == 0) { + return NO; + } + NSURLComponents *components = [[NSURLComponents alloc] initWithString:channelURL]; + NSArray *pathComponents = url.pathComponents; + if (pathComponents.count < 2 || ![pathComponents[1] isEqualToString:@"slink"]) { + return NO; + } + return ([url.host isEqualToString:kSchemeDeepLinkHost] || [url.host isEqualToString:components.host]); +} + +- (BOOL)canWakeUp { + return YES; +} + +- (void)startWithProperties:(NSDictionary *)properties { + // ServerMode 先触发 Launch 事件再请求接口,Launch 事件中只新增 $deeplink_url 属性 + [self trackDeepLinkLaunch:properties]; + [self requestDeepLinkInfo]; +} + +- (NSURLRequest *)buildRequest { + + NSURLComponents *components; + NSString *channelURL = SensorsAnalyticsSDK.sharedInstance.configOptions.customADChannelURL; + SANetwork *network = SensorsAnalyticsSDK.sharedInstance.network; + NSString *key = self.URL.lastPathComponent; + + if ([self.class isCustomDeepLinkURL:self.URL]) { + components = [[NSURLComponents alloc] initWithString:channelURL]; + components.path = [components.path stringByAppendingPathComponent:@"/slink/config/query"]; + components.query = [NSString stringWithFormat:@"key=%@", key]; + } else if ([self.class isNormalDeepLinkURL:self.URL]) { + components = network.baseURLComponents; + components.path = [network.baseURLComponents.path stringByAppendingPathComponent:@"/sdk/deeplink/param"]; + NSString *project = SensorsAnalyticsSDK.sharedInstance.network.project; + components.query = [NSString stringWithFormat:@"key=%@&project=%@&system_type=IOS", key, project]; + } + + if (!components) { + return nil; + } + + NSURL *URL = [components URL]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + request.timeoutInterval = 60; + [request setHTTPMethod:@"GET"]; + return request; +} + +- (void)requestDeepLinkInfo { + NSURLRequest *request = [self buildRequest]; + if (!request) { + return; + } + NSTimeInterval start = NSDate.date.timeIntervalSince1970; + NSURLSessionDataTask *task = [SAHTTPSession.sharedInstance dataTaskWithRequest:request completionHandler:^(NSData *data, NSHTTPURLResponse *response, NSError *error) { + NSTimeInterval interval = (NSDate.date.timeIntervalSince1970 - start); + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + properties[kSAEventPropertyDuration] = [NSString stringWithFormat:@"%.3f", interval]; + + NSDictionary *latestChannels; + + SADeepLinkObject *obj = [[SADeepLinkObject alloc] init]; + obj.appAwakePassedTime = interval * 1000; + obj.success = NO; + + if (response.statusCode == 200 && data) { + NSDictionary *result = [SAJSONUtil JSONObjectWithData:data]; + properties[kSAEventPropertyDeepLinkOptions] = result[kSAResponsePropertyParams]; + properties[kSAEventPropertyDeepLinkFailReason] = result[kSAResponsePropertyMessage]; + properties[kSAEventPropertyADSLinkID] = result[kSAResponsePropertySLinkID]; + + // Result 事件中只需要添加 $utm_content 等属性,不需要添加 $latest_utm_content 等属性 + NSDictionary *channels = [self acquireChannels:result[kSAResponsePropertyChannelParams]]; + [properties addEntriesFromDictionary:channels]; + + // 解析并并转换为 $latest_utm_content 属性,并添加到后续事件所有事件中 + latestChannels = [self acquireLatestChannels:result[kSAResponsePropertyChannelParams]]; + + obj.params = result[kSAResponsePropertyParams]; + obj.success = ([result[kSAResponsePropertyCode] integerValue] == 0); + } else { + NSString *codeMsg = [NSString stringWithFormat:@"http status code: %@",@(response.statusCode)]; + properties[kSAEventPropertyDeepLinkFailReason] = error.localizedDescription ?: codeMsg; + } + + [self trackDeepLinkMatchedResult:properties]; + + if ([self.delegate respondsToSelector:@selector(sendChannels:latestChannels:isDeferredDeepLink:)]) { + // 当前方式不需要获取 channels 信息,只需要保存 latestChannels 信息 + dispatch_async(dispatch_get_main_queue(), ^{ + SADeepLinkCompletion completion = [self.delegate sendChannels:nil latestChannels:latestChannels isDeferredDeepLink:NO]; + if (completion) { + completion(obj); + } + }); + } + }]; + [task resume]; +} + +@end diff --git a/SensorsAnalyticsSDK/CAID/SACAIDUtils.h b/SensorsAnalyticsSDK/Deeplink/SAUserAgent.h similarity index 82% rename from SensorsAnalyticsSDK/CAID/SACAIDUtils.h rename to SensorsAnalyticsSDK/Deeplink/SAUserAgent.h index 4d980cdc..52cfcf66 100644 --- a/SensorsAnalyticsSDK/CAID/SACAIDUtils.h +++ b/SensorsAnalyticsSDK/Deeplink/SAUserAgent.h @@ -1,8 +1,8 @@ // -// SACAIDUtils.h +// SAUserAgent.h // SensorsAnalyticsSDK // -// Created by 彭远洋 on 2021/3/4. +// Created by 彭远洋 on 2021/8/19. // Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,9 @@ NS_ASSUME_NONNULL_BEGIN -@interface SACAIDUtils : NSObject +@interface SAUserAgent : NSObject + ++ (void)loadUserAgentWithCompletion:(void (^)(NSString *))completion; @end diff --git a/SensorsAnalyticsSDK/Deeplink/SAUserAgent.m b/SensorsAnalyticsSDK/Deeplink/SAUserAgent.m new file mode 100644 index 00000000..9449acbb --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SAUserAgent.m @@ -0,0 +1,86 @@ +// +// SAUserAgent.m +// SensorsAnalyticsSDK +// +// Created by 彭远洋 on 2021/8/19. +// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if ! __has_feature(objc_arc) +#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. +#endif + +#import "SAUserAgent.h" +#import +#import "SALog.h" + +@interface SAUserAgent () + +@property (nonatomic, strong) WKWebView *wkWebView; +@property (nonatomic, strong) dispatch_group_t loadUAGroup; +@property (nonatomic, copy) NSString* userAgent; + +@end + +@implementation SAUserAgent + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static SAUserAgent *userAgent; + dispatch_once(&onceToken, ^{ + userAgent = [[SAUserAgent alloc] init]; + }); + return userAgent; +} + ++ (void)loadUserAgentWithCompletion:(void (^)(NSString *))completion { + [[SAUserAgent sharedInstance] loadUserAgentWithCompletion:completion]; +} + +- (void)loadUserAgentWithCompletion:(void (^)(NSString *))completion { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.userAgent.length > 0) { + completion(self.userAgent); + } else if (self.wkWebView) { + dispatch_group_notify(self.loadUAGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + completion(self.userAgent); + }); + } else { + self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectZero]; + self.loadUAGroup = dispatch_group_create(); + dispatch_group_enter(self.loadUAGroup); + + __weak typeof(self) weakSelf = self; + [self.wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable response, NSError *_Nullable error) { + __strong typeof(weakSelf) strongSelf = weakSelf; + + if (error || !response) { + SALogError(@"WKWebView evaluateJavaScript load UA error:%@", error); + completion(nil); + } else { + completion(response); + strongSelf.userAgent = response; + } + // 通过 wkWebView 控制 dispatch_group_leave 的次数 + if (strongSelf.wkWebView) { + dispatch_group_leave(strongSelf.loadUAGroup); + } + strongSelf.wkWebView = nil; + }]; + } + }); +} + +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h b/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h index 2180da21..9c8c2ce3 100644 --- a/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h +++ b/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h @@ -1,9 +1,9 @@ // -// SensorsAnalyticsSDK+Deeplink.h +// SensorsAnalyticsSDK+DeepLink.h // SensorsAnalyticsSDK // // Created by 陈玉国 on 2021/9/11. -// Copyright © 2015-2022 Sensors Data Co., Ltd. All rights reserved. +// Copyright © 2021 Sensors Data Co., Ltd. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,16 +22,32 @@ NS_ASSUME_NONNULL_BEGIN -@interface SensorsAnalyticsSDK (Deeplink) +@interface SADeepLinkObject : NSObject + +/// DeepLink 获取归因数据时的应用内参数 +@property (nonatomic, copy, nullable) NSString *params; + +/// DeepLink 获取的归因数据,当前仅在 Deferred DeepLink 场景下存在 +@property (nonatomic, copy, nullable) NSString *adChannels; + +/// DeepLink 归因数据是否获取成功 +@property (nonatomic, assign) BOOL success; + +/// DeepLink 获取归因数据所用时间,单位毫秒 +@property (nonatomic, assign) NSInteger appAwakePassedTime; + +@end + +@interface SensorsAnalyticsSDK (DeepLink) /** DeepLink 回调函数 @param callback 请求成功后的回调函数 params:创建渠道链接时填写的 App 内参数 - succes:deeplink 唤起结果 + succes:deepLink 唤起结果 appAwakePassedTime:获取渠道信息所用时间 */ -- (void)setDeeplinkCallback:(void(^)(NSString *_Nullable params, BOOL success, NSInteger appAwakePassedTime))callback API_UNAVAILABLE(macos); +- (void)setDeeplinkCallback:(void(^)(NSString *_Nullable params, BOOL success, NSInteger appAwakePassedTime))callback API_UNAVAILABLE(macos) __attribute__((deprecated("已过时,请参考 setDeepLinkCompletion"))); /** 触发 $AppDeepLinkLaunch 事件 @@ -39,9 +55,26 @@ DeepLink 回调函数 */ - (void)trackDeepLinkLaunchWithURL:(NSString *)url API_UNAVAILABLE(macos); +/** +手动触发 Deferred DeepLink 请求,需要在获取设备权限、网络权限后调用 +@param properties 发送请求时自定义参数 +*/ +- (void)requestDeferredDeepLink:(NSDictionary *)properties API_UNAVAILABLE(macos); + +/** + @abstract + DeepLink 回调函数,包含 DeepLink 和 Deferred DeepLink 功能 + + @discussion + 当前 API 在 DeepLink 和 Deferred DeepLink 场景下均可以正常使用。使用此 API 后无需再实现历史 API "setDeepLinkCallback"。 + 若您同时实现了 setDeepLinkCompletion 和 setDeeplinkCallback 两个 API,SDK 内部也只会回调 setDeepLinkCompletion 回调函数。 + @param completion 唤起后的回调函数,当页面跳转成功时,completion 返回值 return YES,反之则 return NO + */ +- (void)setDeepLinkCompletion:(BOOL(^)(SADeepLinkObject *_Nullable obj))completion API_UNAVAILABLE(macos); + @end -@interface SAConfigOptions (Deeplink) +@interface SAConfigOptions (DeepLink) /// DeepLink 中解析出来的参数是否需要保存到本地 @property (nonatomic, assign) BOOL enableSaveDeepLinkInfo API_UNAVAILABLE(macos); @@ -49,6 +82,9 @@ DeepLink 回调函数 /// DeepLink 中用户自定义来源渠道属性 key 值,可传多个。 @property (nonatomic, copy) NSArray *sourceChannels API_UNAVAILABLE(macos); +/// 广告相关功能自定义地址 +@property (nonatomic, copy) NSString *customADChannelURL API_UNAVAILABLE(macos); + @end NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.m b/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.m index 69e35e78..2040fe04 100644 --- a/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.m +++ b/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.m @@ -1,5 +1,5 @@ // -// SensorsAnalyticsSDK+Deeplink.m +// SensorsAnalyticsSDK+DeepLink.m // SensorsAnalyticsSDK // // Created by 陈玉国 on 2021/9/11. @@ -22,17 +22,40 @@ #error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag on this file. #endif -#import "SensorsAnalyticsSDK+Deeplink.h" -#import "SADeeplinkManager.h" +#import "SensorsAnalyticsSDK+DeepLink.h" +#import "SADeepLinkManager.h" +#import "SAConstants+Private.h" +#import "SALog.h" -@implementation SensorsAnalyticsSDK (Deeplink) +@implementation SADeepLinkObject + +@end + +@implementation SensorsAnalyticsSDK (DeepLink) - (void)setDeeplinkCallback:(void(^)(NSString *_Nullable params, BOOL success, NSInteger appAwakePassedTime))callback { - [SADeeplinkManager defaultManager].linkHandlerCallback = callback; + if (!callback) { + return; + } + SADeepLinkManager.defaultManager.oldCompletion = ^BOOL(SADeepLinkObject * _Nonnull object) { + callback(object.params, object.success, object.appAwakePassedTime); + return NO; + }; +} + +- (void)requestDeferredDeepLink:(NSDictionary *)properties { + [SADeepLinkManager.defaultManager requestDeferredDeepLink:properties]; +} + +- (void)setDeepLinkCompletion:(BOOL(^)(SADeepLinkObject *obj))completion { + if (!completion) { + return; + } + SADeepLinkManager.defaultManager.completion = completion; } - (void)trackDeepLinkLaunchWithURL:(NSString *)url { - [[SADeeplinkManager defaultManager] trackDeepLinkLaunchWithURL:url]; + [[SADeepLinkManager defaultManager] trackDeepLinkLaunchWithURL:url]; } @end diff --git a/SensorsAnalyticsSDK/Store/SAFileStorePlugin.m b/SensorsAnalyticsSDK/Store/SAFileStorePlugin.m index 9a79abd0..38b7563c 100644 --- a/SensorsAnalyticsSDK/Store/SAFileStorePlugin.m +++ b/SensorsAnalyticsSDK/Store/SAFileStorePlugin.m @@ -45,7 +45,7 @@ + (NSString *)filePath:(NSString *)key { #pragma mark - SAStorePlugin - (NSArray *)storeKeys { - return @[@"com.sensorsdata.caid.cache", @"$channel_device_info", @"login_id", @"distinct_id", @"com.sensorsdata.loginidkey", @"com.sensorsdata.identities", @"first_day", @"super_properties", @"latest_utms", @"SAEncryptSecretKey", @"SAVisualPropertiesConfig", @"SASessionModel"]; + return @[@"$channel_device_info", @"login_id", @"distinct_id", @"com.sensorsdata.loginidkey", @"com.sensorsdata.identities", @"first_day", @"super_properties", @"latest_utms", @"SAEncryptSecretKey", @"SAVisualPropertiesConfig", @"SASessionModel"]; } - (NSString *)type { diff --git a/SensorsAnalyticsTests/Builder/SAIdentifierTest.m b/SensorsAnalyticsTests/Builder/SAIdentifierTest.m index 2567877c..06aef89e 100644 --- a/SensorsAnalyticsTests/Builder/SAIdentifierTest.m +++ b/SensorsAnalyticsTests/Builder/SAIdentifierTest.m @@ -58,7 +58,7 @@ @implementation SAIdentifierTest - (void)setUp { NSString *label = [NSString stringWithFormat:@"sensorsdata.readWriteQueue.%p", self]; _readWriteQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_SERIAL); - _identifier = [[SAIdentifier alloc] initWithQueue:_readWriteQueue loginIDKey:kLoginId]; + _identifier = [[SAIdentifier alloc] initWithQueue:_readWriteQueue]; [_identifier logout]; _deviceId = _identifier.anonymousId; } @@ -111,7 +111,7 @@ - (void)testLoginIdGreaterThanMaxLength { for (int i = 0; i < kSAPropertyValueMaxLength + 5; i++) { [str appendString:@"a"]; } - XCTAssertTrue([_identifier isValidLoginId:str]); + XCTAssertTrue([_identifier isValidForLogin:kLoginId value:str]); } - (void)testLoginIdLessThanMaxLength { @@ -119,35 +119,35 @@ - (void)testLoginIdLessThanMaxLength { for (int i = 0; i < kSAPropertyValueMaxLength - 5; i++) { [str appendString:@"a"]; } - XCTAssertTrue([_identifier isValidLoginId:str]); + XCTAssertTrue([_identifier isValidForLogin:kLoginId value:str]); } - (void)testLoginWithLoginId { - [_identifier login:@"new_login_id"]; - XCTAssertFalse([_identifier isValidLoginId:_identifier.loginId]); + [_identifier loginWithKey:kLoginId loginId:@"new_login_id"]; + XCTAssertFalse([_identifier isValidForLogin:kLoginId value:_identifier.loginId]); } - (void)testLoginWithAnonymousId { - XCTAssertFalse([_identifier isValidLoginId:_identifier.anonymousId]); + XCTAssertFalse([_identifier isValidForLogin:kLoginId value:_identifier.anonymousId]); } - (void)testLoginIdAfterLogin { - [_identifier login:@"new_login_id"]; + [_identifier loginWithKey:kLoginId loginId:@"new_login_id"]; XCTAssertTrue([_identifier.loginId isEqualToString:@"new_login_id"]); } - (void)testDistinctIdAfterLogin { - [_identifier login:@"new_login_id"]; + [_identifier loginWithKey:kLoginId loginId:@"new_login_id"]; XCTAssertTrue([_identifier.distinctId isEqualToString:@"new_login_id"]); } - (void)testLoginIdAfterLoginEmptyString { - BOOL result = [_identifier isValidLoginId:@""]; + BOOL result = [_identifier isValidForLogin:kLoginId value:@""]; XCTAssertFalse(result); } - (void)testDistinctIdAfterLoginEmptyString { - [_identifier login:@""]; + [_identifier loginWithKey:kLoginId loginId:@""]; XCTAssertTrue([_identifier.distinctId isEqualToString:_identifier.anonymousId]); } @@ -157,7 +157,7 @@ - (void)testResetAnonymousId { } - (void)testLogout { - [_identifier login:@"new_login_id"]; + [_identifier loginWithKey:kLoginId loginId:@"new_login_id"]; [_identifier logout]; XCTAssertNil(_identifier.loginId); } @@ -166,7 +166,7 @@ - (void)testLogout { - (void)testAddIdentityForInvalidKey { NSArray *array = @[@"111", @"date", kIDFV, kAnonymousId, kLoginId, kUUID, @{}, @"xcx###"]; for (NSString *key in array) { - BOOL result = [_identifier isValidIdentity:key value:@""]; + BOOL result = [_identifier isValidForBind:key value:@""]; XCTAssertFalse(result); } } @@ -174,25 +174,25 @@ - (void)testAddIdentityForInvalidKey { - (void)testAddIdentityForValidKey { NSArray *array = @[@"xxx111", kMobile]; for (NSString *key in array) { - BOOL result = [_identifier isValidIdentity:key value:@"value"]; + BOOL result = [_identifier isValidForBind:key value:@"value"]; XCTAssertTrue(result); } } - (void)testAddIdentityForInvalidValue { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; NSArray *array = @[@"", kLoginIdValue, @{}]; for (NSString *value in array) { - BOOL result = [_identifier isValidIdentity:kLoginId value:value]; + BOOL result = [_identifier isValidForBind:kLoginId value:value]; XCTAssertFalse(result); } } - (void)testAddIdentityForValidValue { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; NSArray *array = @[kCustomKey, @"newLoginId11"]; for (NSString *value in array) { - BOOL result = [_identifier isValidIdentity:kCustomKey value:value]; + BOOL result = [_identifier isValidForBind:kCustomKey value:value]; XCTAssertTrue(result); } } @@ -200,7 +200,7 @@ - (void)testAddIdentityForValidValue { - (void)testRemoveIdentityForInvalidKey { NSArray *array = @[@"111", @"date", kIDFV, kAnonymousId, kLoginId, kUUID, @{}, @"xcx###"]; for (NSString *key in array) { - BOOL result = [_identifier isValidIdentity:key value:@""]; + BOOL result = [_identifier isValidForBind:key value:@""]; XCTAssertFalse(result); } } @@ -208,37 +208,37 @@ - (void)testRemoveIdentityForInvalidKey { - (void)testRemoveIdentityForValidKey { NSArray *array = @[@"xxx111", kMobile, kEmail]; for (NSString *key in array) { - BOOL result = [_identifier isValidIdentity:key value:@"value"]; + BOOL result = [_identifier isValidForBind:key value:@"value"]; XCTAssertTrue(result); } } - (void)testRemoveIdentityForInvalidValue { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; NSArray *array = @[@"", kLoginIdValue, @{}]; for (NSString *value in array) { - BOOL result = [_identifier isValidIdentity:kLoginId value:value]; + BOOL result = [_identifier isValidForBind:kLoginId value:value]; XCTAssertFalse(result); } } - (void)testRemoveIdentityForValidValue { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; NSArray *array = @[kCustomKey, @"newLoginId11"]; for (NSString *value in array) { - BOOL result = [_identifier isValidIdentity:kCustomKey value:value]; + BOOL result = [_identifier isValidForBind:kCustomKey value:value]; XCTAssertTrue(result); } } #pragma mark - identities - login & logout - (void)testIdentitiesAfterLogin { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; XCTAssertTrue([_identifier.identities[kLoginId] isEqualToString:kLoginIdValue]); } - (void)testIdentitiesAfterLogout { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; [_identifier logout]; XCTAssertNil(_identifier.identities[kLoginId]); } @@ -277,7 +277,7 @@ - (void)testAddIdentityForLogin { } - (void)testAddIdentityForDifferentLoginId { - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; NSString *newId = @"xxxLoginId"; [_identifier bindIdentity:kLoginId value:newId]; XCTAssertTrue([_identifier.identities[kLoginId] isEqualToString:newId]); @@ -326,7 +326,7 @@ - (void)testEventAfterLogin { [_identifier bindIdentity:kEmail value:kEmailValue]; [_identifier bindIdentity:kCustomKey value:kCustomValue]; - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; // 登录事件 NSDictionary *identities = [_identifier identitiesWithEventType:kSAEventTypeSignup]; XCTAssertTrue(identities.allKeys.count == 5); @@ -495,7 +495,7 @@ - (void)testH5EventAfterBindForExistKey { - (void)testH5EventAfterSignUpForNotSign { // H5 登录事件 - [_identifier login:kLoginIdValue]; + [_identifier loginWithKey:kLoginId loginId:kLoginIdValue]; NSDictionary *identities = [_identifier mergeH5Identities:@{kLoginId:kLoginIdValue} eventType:kSAEventTypeSignup]; XCTAssertTrue(identities.allKeys.count == 2); XCTAssertTrue([identities[kLoginId] isEqualToString:kLoginIdValue]); @@ -519,10 +519,10 @@ - (void)testH5EventAfterSignUpForNotSign { - (void)testH5EventAfterSignUpForSigned { NSString *oldValue = kLoginIdValue; NSString *newValue = @"xxxNewLoginId"; - [_identifier login:oldValue]; + [_identifier loginWithKey:kLoginId loginId:oldValue]; // H5 登录事件 - [_identifier login:newValue]; + [_identifier loginWithKey:kLoginId loginId:newValue]; NSDictionary *identities = [_identifier mergeH5Identities:@{kLoginId:newValue} eventType:kSAEventTypeSignup]; XCTAssertTrue(identities.allKeys.count == 2); XCTAssertTrue([identities[kLoginId] isEqualToString:newValue]); @@ -544,7 +544,7 @@ - (void)testH5EventAfterSignUpForSigned { } - (void)testH5EventAfterNativeSignUpAndBind { - [_identifier login:kLoginId]; + [_identifier loginWithKey:kLoginId loginId:kLoginId]; [_identifier bindIdentity:kCustomKey value:kCustomValue]; // H5 事件 diff --git a/SensorsAnalyticsTests/SALinkHandlerTests.m b/SensorsAnalyticsTests/SALinkHandlerTests.m index f04babb9..e66a4535 100644 --- a/SensorsAnalyticsTests/SALinkHandlerTests.m +++ b/SensorsAnalyticsTests/SALinkHandlerTests.m @@ -23,13 +23,13 @@ #endif #import +#import "SADeepLinkManager.h" #import "SensorsAnalyticsSDK+Deeplink.h" -#import "SADeeplinkManager.h" #import "SAConfigOptions.h" @interface SALinkHandlerTests : XCTestCase -@property (nonatomic, strong) SADeeplinkManager *linkHandler; +@property (nonatomic, strong) SADeepLinkManager *linkHandler; @end