diff --git a/SensorsAnalyticsSDK.podspec b/SensorsAnalyticsSDK.podspec index aaae9226..66be07e5 100644 --- a/SensorsAnalyticsSDK.podspec +++ b/SensorsAnalyticsSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SensorsAnalyticsSDK" - s.version = "4.4.2" + s.version = "4.4.3" 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}" } @@ -37,7 +37,7 @@ Pod::Spec.new do |s| c.public_header_files = 'SensorsAnalyticsSDK/JSBridge/SensorsAnalyticsSDK+JavaScriptBridge.h' c.source_files = 'SensorsAnalyticsSDK/Core/SAAlertController.{h,m}', 'SensorsAnalyticsSDK/JSBridge/**/*.{h,m}' c.ios.source_files = 'SensorsAnalyticsSDK/RemoteConfig/**/*.{h,m}', 'SensorsAnalyticsSDK/ChannelMatch/**/*.{h,m}', 'SensorsAnalyticsSDK/Encrypt/**/*.{h,m}', 'SensorsAnalyticsSDK/Deeplink/**/*.{h,m}', 'SensorsAnalyticsSDK/DebugMode/**/*.{h,m}', 'SensorsAnalyticsSDK/Core/SAAlertController.h' - c.ios.public_header_files = 'SensorsAnalyticsSDK/{Encrypt,RemoteConfig,ChannelMatch,Deeplink,DebugMode}/{SAConfigOptions,SensorsAnalyticsSDK}+*.h', 'SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h', 'SensorsAnalyticsSDK/Encrypt/SASecretKey.h' + c.ios.public_header_files = 'SensorsAnalyticsSDK/{Encrypt,RemoteConfig,ChannelMatch,Deeplink,DebugMode}/{SAConfigOptions,SensorsAnalyticsSDK}+*.h', 'SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h', 'SensorsAnalyticsSDK/Encrypt/SASecretKey.h', 'SensorsAnalyticsSDK/Deeplink/SASlinkCreator.h' end s.subspec 'Core' do |c| diff --git a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj index a90ece77..1dd7b222 100644 --- a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj +++ b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj @@ -445,6 +445,10 @@ F2B643F62832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = F2B643F42832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.h */; settings = {ATTRIBUTES = (Public, ); }; }; F2B643F72832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = F2B643F52832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.m */; }; F2CFD14726EB04A8007A9253 /* SAConfigOptions+RemoteConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = F2CFD14526EB04A8007A9253 /* SAConfigOptions+RemoteConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F2E364872876EE94008D9151 /* SASlinkCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E364852876EE94008D9151 /* SASlinkCreator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F2E364882876EE94008D9151 /* SASlinkCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E364862876EE94008D9151 /* SASlinkCreator.m */; }; + F2E36483287682E6008D9151 /* SADeviceWhiteList.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E36481287682E6008D9151 /* SADeviceWhiteList.h */; }; + F2E36484287682E6008D9151 /* SADeviceWhiteList.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E36482287682E6008D9151 /* SADeviceWhiteList.m */; }; F2E4AB9D26EC86C800BA7F01 /* SensorsAnalyticsSDK+Location.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E4AB9B26EC86C800BA7F01 /* SensorsAnalyticsSDK+Location.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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, ); }; }; @@ -941,6 +945,10 @@ F2B643F42832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SensorsAnalyticsSDK+SAAppExtension.h"; sourceTree = ""; }; F2B643F52832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SensorsAnalyticsSDK+SAAppExtension.m"; sourceTree = ""; }; F2CFD14526EB04A8007A9253 /* SAConfigOptions+RemoteConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SAConfigOptions+RemoteConfig.h"; sourceTree = ""; }; + F2E364852876EE94008D9151 /* SASlinkCreator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SASlinkCreator.h; sourceTree = ""; }; + F2E364862876EE94008D9151 /* SASlinkCreator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SASlinkCreator.m; sourceTree = ""; }; + F2E36481287682E6008D9151 /* SADeviceWhiteList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SADeviceWhiteList.h; sourceTree = ""; }; + F2E36482287682E6008D9151 /* SADeviceWhiteList.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SADeviceWhiteList.m; sourceTree = ""; }; F2E4AB9B26EC86C800BA7F01 /* SensorsAnalyticsSDK+Location.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SensorsAnalyticsSDK+Location.h"; sourceTree = ""; }; 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 = ""; }; @@ -1818,6 +1826,10 @@ A8BCC4D826872A3F00B72040 /* SADeepLinkManager.m */, F2E4ABA326ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.h */, F2E4ABA426ECAC5400BA7F01 /* SensorsAnalyticsSDK+DeepLink.m */, + F2E364852876EE94008D9151 /* SASlinkCreator.h */, + F2E364862876EE94008D9151 /* SASlinkCreator.m */, + F2E36481287682E6008D9151 /* SADeviceWhiteList.h */, + F2E36482287682E6008D9151 /* SADeviceWhiteList.m */, ); path = Deeplink; sourceTree = ""; @@ -2109,6 +2121,7 @@ 4DD1284B25F872A4008C0B1E /* SAWebElementView.h in Headers */, F277F5C925CF9A43009B5CE6 /* SANotificationUtil.h in Headers */, A8CC223E2685E50C00E96A03 /* SARemoteConfigModel.h in Headers */, + F2E36483287682E6008D9151 /* SADeviceWhiteList.h in Headers */, 881A41A0253D7B4F00854F69 /* SensorsAnalyticsSDK+Private.h in Headers */, A8356DC22656459A00FD64AA /* SensorsAnalyticsSDK+SAAutoTrack.h in Headers */, F2066973271FFEE10002ABDF /* UIViewController+SAPageLeave.h in Headers */, @@ -2184,6 +2197,7 @@ F277F5C325CF9A43009B5CE6 /* UIApplication+SAPushClick.h in Headers */, FCBECDF727DEDF6400361D6C /* SADeepLinkEventProcessor.h in Headers */, F277F5E425CFCE56009B5CE6 /* NSObject+SADelegateProxy.h in Headers */, + F2E364872876EE94008D9151 /* SASlinkCreator.h in Headers */, F277F5C325CF9A43009B5CE6 /* UIApplication+SAPushClick.h in Headers */, F2B643F62832302F00544BD2 /* SensorsAnalyticsSDK+SAAppExtension.h in Headers */, F277F5E425CFCE56009B5CE6 /* NSObject+SADelegateProxy.h in Headers */, @@ -2505,6 +2519,7 @@ 4DD1286125F872A4008C0B1E /* SAPropertyDescription.m in Sources */, 4DA89BC425C2BC1E003ABA43 /* SAReachability.m in Sources */, F277F5CB25CF9A43009B5CE6 /* SAAppPushConstants.m in Sources */, + F2E364882876EE94008D9151 /* SASlinkCreator.m in Sources */, 4DD1286325F872A4008C0B1E /* SAVisualizedAutoTrackObjectSerializer.m in Sources */, 45BD80CF26F0B49700DCC759 /* SAThreadSafeDictionary.m in Sources */, 881A4193253D7B4F00854F69 /* SensorsAnalyticsSDK.m in Sources */, @@ -2546,6 +2561,7 @@ A8356DDA2656459A00FD64AA /* SAAppStartTracker.m in Sources */, 4D4DB2CD25B7D19C00938842 /* SAMethodHelper.m in Sources */, F23CA0092701715E002EEACA /* WKWebView+SABridge.m in Sources */, + F2E36484287682E6008D9151 /* SADeviceWhiteList.m in Sources */, 88E6BEDE278ECE5E006B1E4C /* SAAppVersionPropertyPlugin.m in Sources */, 883BAAB12669CD18008105D2 /* SAExceptionManager.m in Sources */, 4DD1286625F872A4008C0B1E /* SAVisualizedAbstractMessage.m in Sources */, diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m index ae08e888..47ee61f0 100755 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m @@ -62,7 +62,7 @@ #import "SASessionPropertyPlugin.h" #import "SAEventStore.h" -#define VERSION @"4.4.2" +#define VERSION @"4.4.3" void *SensorsAnalyticsQueueTag = &SensorsAnalyticsQueueTag; diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h index 8b1dd3ff..fcf3c158 100644 --- a/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.h @@ -72,3 +72,49 @@ extern NSString *const kSAResponsePropertyParameter; extern NSString *const kSAResponsePropertyADChannel; NSSet* sensorsdata_preset_channel_keys(void); + +//dynamic slink related message + +//dynamic slink related code +extern NSInteger kSADynamicSlinkStatusCodeSuccess; +extern NSInteger kSADynamicSlinkStatusCodeLessParams; +extern NSInteger kSADynamicSlinkStatusCodeNoNetwork; +extern NSInteger kSADynamicSlinkStatusCodeoNoDomain; +extern NSInteger kSADynamicSlinkStatusCodeResponseError; + +//dynamic slink event name and properties +extern NSString *const kSADynamicSlinkEventName; +extern NSString *const kSADynamicSlinkEventPropertyChannelType; +extern NSString *const kSADynamicSlinkEventPropertyChannelName; +extern NSString *const kSADynamicSlinkEventPropertySource; +extern NSString *const kSADynamicSlinkEventPropertyData; +extern NSString *const kSADynamicSlinkEventPropertyShortURL; +extern NSString *const kSADynamicSlinkEventPropertyStatus; +extern NSString *const kSADynamicSlinkEventPropertyMessage; +extern NSString *const kSADynamicSlinkEventPropertyID; +extern NSString *const kSADynamicSlinkEventPropertyTemplateID; +extern NSString *const kSADynamicSlinkEventPropertyType; +extern NSString *const kSADynamicSlinkEventPropertyTypeDynamic; + +//dynamic slink API path +extern NSString *const kSADynamicSlinkAPIPath; + +//dynamic slink API params +extern NSString *const kSADynamicSlinkParamProject; +extern NSString *const kSADynamicSlinkParamTemplateID; +extern NSString *const kSADynamicSlinkParamType; +extern NSString *const kSADynamicSlinkParamName; +extern NSString *const kSADynamicSlinkParamChannelType; +extern NSString *const kSADynamicSlinkParamChannelName; +extern NSString *const kSADynamicSlinkParamFixedUTM; +extern NSString *const kSADynamicSlinkParamUTMSource; +extern NSString *const kSADynamicSlinkParamUTMCampaign; +extern NSString *const kSADynamicSlinkParamUTMMedium; +extern NSString *const kSADynamicSlinkParamUTMTerm; +extern NSString *const kSADynamicSlinkParamUTMContent; +extern NSString *const kSADynamicSlinkParamCustom; +extern NSString *const kSADynamicSlinkParamRoute; +extern NSString *const kSADynamicSlinkParamURIScheme; +extern NSString *const kSADynamicSlinkParamLandingPageType; +extern NSString *const kSADynamicSlinkParamLandingPage; +extern NSString *const kSADynamicSlinkParamJumpAddress; diff --git a/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m index a77b8cd8..3aed212e 100644 --- a/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m +++ b/SensorsAnalyticsSDK/Deeplink/SADeepLinkConstants.m @@ -80,3 +80,48 @@ NSSet* sensorsdata_preset_channel_keys(void) { return [NSSet setWithObjects:@"utm_campaign", @"utm_content", @"utm_medium", @"utm_source", @"utm_term", nil]; } + +//dynamic slink related code +NSInteger kSADynamicSlinkStatusCodeSuccess= 0; +NSInteger kSADynamicSlinkStatusCodeLessParams = 10001; +NSInteger kSADynamicSlinkStatusCodeNoNetwork = 10002; +NSInteger kSADynamicSlinkStatusCodeoNoDomain = 10003; +NSInteger kSADynamicSlinkStatusCodeResponseError = 10004; + +//dynamic slink event name and properties +NSString *const kSADynamicSlinkEventName = @"$AdDynamicSlinkCreate"; +NSString *const kSADynamicSlinkEventPropertyChannelType = @"$ad_dynamic_slink_channel_type"; +NSString *const kSADynamicSlinkEventPropertyChannelName = @"$ad_dynamic_slink_channel_name"; +NSString *const kSADynamicSlinkEventPropertySource = @"$ad_dynamic_slink_source"; +NSString *const kSADynamicSlinkEventPropertyData = @"$ad_dynamic_slink_data"; +NSString *const kSADynamicSlinkEventPropertyShortURL = @"$ad_dynamic_slink_short_url"; +NSString *const kSADynamicSlinkEventPropertyStatus = @"$ad_dynamic_slink_status"; +NSString *const kSADynamicSlinkEventPropertyMessage = @"$ad_dynamic_slink_msg"; +NSString *const kSADynamicSlinkEventPropertyID = @"$ad_slink_id"; +NSString *const kSADynamicSlinkEventPropertyTemplateID = @"$ad_slink_template_id"; +NSString *const kSADynamicSlinkEventPropertyType = @"$ad_slink_type"; +NSString *const kSADynamicSlinkEventPropertyTypeDynamic = @"dynamic"; + +//dynamic slink API path +NSString *const kSADynamicSlinkAPIPath = @"slink/dynamic/links"; + +//dynamic slink API params +NSString *const kSADynamicSlinkParamProject = @"project_name"; +NSString *const kSADynamicSlinkParamTemplateID = @"slink_template_id"; +NSString *const kSADynamicSlinkParamType = @"slink_type"; +NSString *const kSADynamicSlinkParamName = @"name"; +NSString *const kSADynamicSlinkParamChannelType = @"channel_type"; +NSString *const kSADynamicSlinkParamChannelName = @"channel_name"; +NSString *const kSADynamicSlinkParamFixedUTM = @"fixed_param"; +NSString *const kSADynamicSlinkParamUTMSource = @"channel_utm_source"; +NSString *const kSADynamicSlinkParamUTMCampaign = @"channel_utm_campaign"; +NSString *const kSADynamicSlinkParamUTMMedium = @"channel_utm_medium"; +NSString *const kSADynamicSlinkParamUTMTerm = @"channel_utm_term"; +NSString *const kSADynamicSlinkParamUTMContent = @"channel_utm_content"; +NSString *const kSADynamicSlinkParamCustom = @"custom_param"; +NSString *const kSADynamicSlinkParamRoute = @"route_param"; +NSString *const kSADynamicSlinkParamURIScheme = @"uri_scheme_suffix"; +NSString *const kSADynamicSlinkParamLandingPageType = @"landing_page_type"; +NSString *const kSADynamicSlinkParamLandingPage = @"other_landing_page_map"; +NSString *const kSADynamicSlinkParamJumpAddress = @"jump_address"; + diff --git a/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m b/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m index e42d1a31..bee4c748 100644 --- a/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m +++ b/SensorsAnalyticsSDK/Deeplink/SADeeplinkManager.m @@ -42,6 +42,7 @@ #import "SAFirstDayPropertyPlugin.h" #import "SAPropertyPluginManager.h" #import "SALatestUtmPropertyPlugin.h" +#import "SADeviceWhiteList.h" @interface SADeepLinkManager () @@ -55,6 +56,8 @@ @interface SADeepLinkManager () /// 本次冷启动时的 DeepLinkURL @property (nonatomic, strong) NSURL *deepLinkURL; +@property (nonatomic, strong) SADeviceWhiteList *whiteList; + @end @implementation SADeepLinkManager @@ -93,6 +96,7 @@ - (instancetype)init { } else { [self disableDeferredDeepLink]; } + _whiteList = [[SADeviceWhiteList alloc] init]; } return self; } @@ -265,11 +269,17 @@ - (BOOL)canHandleURL:(NSURL *)url { if (![url isKindOfClass:NSURL.class]) { return NO; } + if ([self.whiteList canHandleURL:url]) { + return YES; + } SADeepLinkProcessor *processor = [SADeepLinkProcessorFactory processorFromURL:url customChannelKeys:self.customChannelKeys]; return processor.canWakeUp; } - (BOOL)handleURL:(NSURL *)url { + if ([self.whiteList canHandleURL:url]) { + return [self.whiteList handleURL:url]; + } // 当 url 和 _deepLinkURL 相同时,则表示本次触发是冷启动触发,已通过 acquireColdLaunchDeepLinkInfo 方法处理,这里不需要重复处理 NSString *absoluteString = _deepLinkURL.absoluteString; _deepLinkURL = nil; diff --git a/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m b/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m index b0726ab2..68a14819 100644 --- a/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m +++ b/SensorsAnalyticsSDK/Deeplink/SADeferredDeepLinkProcessor.m @@ -78,6 +78,8 @@ - (void)requestForDeferredDeepLink:(NSURLRequest *)request { properties[kSAEventPropertyADChannel] = result[kSAResponsePropertyADChannel]; properties[kSAEventPropertyDeepLinkFailReason] = result ? result[kSAResponsePropertyMessage] : @"response is null"; properties[kSAEventPropertyADSLinkID] = result[kSAResponsePropertySLinkID]; + properties[kSADynamicSlinkEventPropertyTemplateID] = result[kSADynamicSlinkParamTemplateID]; + properties[kSADynamicSlinkEventPropertyType] = result[kSADynamicSlinkParamType];; obj.params = result[kSAResponsePropertyParameter]; obj.adChannels = result[kSAResponsePropertyADChannel]; obj.success = (result[kSAResponsePropertyCode] && [result[kSAResponsePropertyCode] integerValue] == 0); @@ -118,6 +120,8 @@ - (void)requestForDeferredDeepLink:(NSURLRequest *)request { NSMutableDictionary *jumpProps = [NSMutableDictionary dictionary]; jumpProps[kSAEventPropertyDeepLinkOptions] = obj.params; jumpProps[kSAEventPropertyADSLinkID] = slinkID; + jumpProps[kSADynamicSlinkEventPropertyTemplateID] = properties[kSADynamicSlinkEventPropertyTemplateID]; + jumpProps[kSADynamicSlinkEventPropertyType] = properties[kSADynamicSlinkEventPropertyType]; [SensorsAnalyticsSDK.sharedInstance trackEventObject:object properties:jumpProps]; diff --git a/SensorsAnalyticsSDK/Deeplink/SADeviceWhiteList.h b/SensorsAnalyticsSDK/Deeplink/SADeviceWhiteList.h new file mode 100644 index 00000000..19da7564 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeviceWhiteList.h @@ -0,0 +1,31 @@ +// +// SADeviceWhiteList.h +// SensorsAnalyticsSDK +// +// Created by 陈玉国 on 2022/7/7. +// 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 +#import "SAModuleProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SADeviceWhiteList : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SADeviceWhiteList.m b/SensorsAnalyticsSDK/Deeplink/SADeviceWhiteList.m new file mode 100644 index 00000000..b4f1e1da --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SADeviceWhiteList.m @@ -0,0 +1,112 @@ +// +// SADeviceWhiteList.m +// SensorsAnalyticsSDK +// +// Created by 陈玉国 on 2022/7/7. +// 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 "SADeviceWhiteList.h" +#import "SAAlertController.h" +#import "SAURLUtils.h" +#import "SensorsAnalyticsSDK+Private.h" +#import "SAJSONUtil.h" +#import "SAIdentifier.h" +#import "SAConstants+Private.h" + + +static NSString * const kSADeviceWhiteListHost = @"adsScanDeviceInfo"; +static NSString * const kSADeviceWhiteListQueryParamProjectName = @"project"; +static NSString * const kSADeviceWhiteListQueryParamInfoId = @"info_id"; +static NSString * const kSADeviceWhiteListQueryParamDeviceType = @"device_type"; +static NSString * const kSADeviceWhiteListQueryParamApiUrl = @"apiurl"; + +@implementation SADeviceWhiteList + +- (BOOL)canHandleURL:(NSURL *)url { + return [url.host isEqualToString:kSADeviceWhiteListHost]; +} + +- (BOOL)handleURL:(NSURL *)url { + NSDictionary *query = [SAURLUtils decodeRueryItemsWithURL:url]; + if (!query) { + return NO; + } + NSString *projectName = query[kSADeviceWhiteListQueryParamProjectName]; + if (![projectName isEqualToString:[SensorsAnalyticsSDK sdkInstance].network.project]) { + [self showAlertWithMessage:SALocalizedString(@"SADeviceWhiteListMessageProject")]; + return NO; + } + NSString *deviceType = query[kSADeviceWhiteListQueryParamDeviceType]; + //1 iOS,2 Android + if (![deviceType isEqualToString:@"1"]) { + [self showAlertWithMessage:SALocalizedString(@"SADeviceWhiteListMessageDeviceType")]; + return NO; + } + NSString *apiUrlString = query[kSADeviceWhiteListQueryParamApiUrl]; + if (!apiUrlString) { + return NO; + } + NSURL *apiUrl = [NSURL URLWithString:apiUrlString]; + if (!apiUrl) { + return NO; + } + + NSString *infoId = query[kSADeviceWhiteListQueryParamInfoId]; + NSDictionary *params = @{kSADeviceWhiteListQueryParamInfoId: infoId, + kSADeviceWhiteListQueryParamDeviceType:@"1", + @"project_name":projectName, + @"ios_idfa":[SAIdentifier idfa] ? : @"", + @"ios_idfv": [SAIdentifier idfv] ? : @""}; + [self addWhiteListWithUrl:apiUrl params:params]; + return YES; +} + +- (void)showAlertWithMessage:(NSString *)message { + dispatch_async(dispatch_get_main_queue(), ^{ + SAAlertController *alert = [[SAAlertController alloc] initWithTitle:SALocalizedString(@"SADeviceWhiteListTitle") message:message preferredStyle:SAAlertControllerStyleAlert]; + [alert addActionWithTitle:SALocalizedString(@"SAAlertOK") style:SAAlertActionStyleDefault handler:nil]; + [alert show]; + }); +} + +- (void)addWhiteListWithUrl:(NSURL *)url params:(NSDictionary *)params { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + request.timeoutInterval = 30; + request.HTTPBody = [SAJSONUtil dataWithJSONObject:params]; + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + + NSURLSessionDataTask *task = [[SAHTTPSession sharedInstance] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) { + //HTTP Status 200 and code 0 + if (response.statusCode == 200) { + NSDictionary *result = [SAJSONUtil JSONObjectWithData:data]; + if ([result isKindOfClass:[NSDictionary class]] && + [result[@"code"] isKindOfClass:[NSNumber class]] && + [result[@"code"] integerValue] == 0) { + [self showAlertWithMessage:SALocalizedString(@"SADeviceWhiteListMessageRequestSuccess")]; + return; + } + } + [self showAlertWithMessage:SALocalizedString(@"SADeviceWhiteListMessageRequestFailure")]; + }]; + [task resume]; +} + +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m b/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m index 5be463ce..6f309b4c 100644 --- a/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m +++ b/SensorsAnalyticsSDK/Deeplink/SARequestDeepLinkProcessor.m @@ -26,6 +26,7 @@ #import "SADeepLinkConstants.h" #import "SensorsAnalyticsSDK+Private.h" #import "SAJSONUtil.h" +#import "SAConstants+Private.h" static NSString *const kSchemeDeepLinkHost = @"sensorsdata"; @@ -153,6 +154,8 @@ - (void)requestDeepLinkInfo { NSString *errorMsg = result[kSAResponsePropertyErrorMessage] ?: result[kSAResponsePropertyErrorMsg]; properties[kSAEventPropertyDeepLinkFailReason] = errorMsg; properties[kSAEventPropertyADSLinkID] = result[kSAResponsePropertySLinkID]; + properties[kSADynamicSlinkEventPropertyTemplateID] = result[kSADynamicSlinkParamTemplateID]; + properties[kSADynamicSlinkEventPropertyType] = result[kSADynamicSlinkParamType]; // Result 事件中只需要添加 $utm_content 等属性,不需要添加 $latest_utm_content 等属性 NSDictionary *channels = [self acquireChannels:result[kSAResponsePropertyChannelParams]]; diff --git a/SensorsAnalyticsSDK/Deeplink/SASlinkCreator.h b/SensorsAnalyticsSDK/Deeplink/SASlinkCreator.h new file mode 100644 index 00000000..dab68118 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SASlinkCreator.h @@ -0,0 +1,116 @@ +// +// SASlinkCreator.h +// SensorsAnalyticsSDK +// +// Created by 陈玉国 on 2022/7/7. +// 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 + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, SATLandingPageType) { + SATLandingPageTypeIntelligence = 0, + SATLandingPageTypeOther, + SATLandingPageTypeUndefined, +}; + +@interface SATUTMProperties : NSObject + +/// channel_utm_campaign +@property (nonatomic, copy, nullable) NSString *campaign; + +/// channel_utm_source +@property (nonatomic, copy, nullable) NSString *source; + +/// channel_utm_medium +@property (nonatomic, copy, nullable) NSString *medium; + +/// channel_utm_term +@property (nonatomic, copy, nullable) NSString *term; + +/// channel_utm_content +@property (nonatomic, copy, nullable) NSString *content; + +@end + +@interface SASlinkResponse : NSObject + +/// status code when creating slink, such as 0 indicate that slink was created successfully +@property (nonatomic, assign, readonly) NSInteger statusCode; + +/// message when creaing slink, each status code matched a message +@property (nonatomic, copy, readonly) NSString *message; + +/// created slink, maybe nil +@property (nonatomic, copy, nullable, readonly) NSString *slink; + +/// slink ID, maybe nil +@property (nonatomic, copy, nullable, readonly) NSString *slinkID; + +/// common redirect uri, once slink created failed, use this instead +@property (nonatomic, copy, readonly) NSString *commonRedirectURI; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +@interface SASlinkCreator : NSObject + +/// name for slink +@property (nonatomic, copy, nullable) NSString *name; + +/// url scheme suffix, such as "11/8A/X" +@property (nonatomic, copy, nullable) NSString *uriSchemeSuffix; + +/// landing page type, such as intelligence or other +@property (nonatomic, assign) SATLandingPageType landingPageType; + +/// redirect url once slink opened on other devices, such as PC, not mobile devices +@property (nonatomic, copy, nullable) NSString *redirectURLOnOtherDevice; + +/// route param for slink, such as "a=123&b=test" +@property (nonatomic, copy, nullable) NSString *routeParam; + +/// landing page settings +@property (nonatomic, copy, nullable) NSDictionary *landingPage; + +/// custom params for slink +@property (nonatomic, copy, nullable) NSDictionary *customParams; + +/// utm properties +@property (nonatomic, strong, nullable) SATUTMProperties *utmProperties; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/// init method for slink creator +/// @param templateID slink template ID +/// @param channelName channel name +/// @param commonRedirectURI common redirect url +/// @param accessToken access token +- (instancetype)initWithTemplateID:(NSString *)templateID channelName:(NSString *)channelName commonRedirectURI:(NSString *)commonRedirectURI accessToken:(NSString *)accessToken; + + +/// create slink +/// @param completion completion when creating slink +- (void)createSlinkWithCompletion:(nullable void (^)(SASlinkResponse *response))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Deeplink/SASlinkCreator.m b/SensorsAnalyticsSDK/Deeplink/SASlinkCreator.m new file mode 100644 index 00000000..acc01fa7 --- /dev/null +++ b/SensorsAnalyticsSDK/Deeplink/SASlinkCreator.m @@ -0,0 +1,281 @@ +// +// SASlinkCreator.m +// SensorsAnalyticsSDK +// +// Created by 陈玉国 on 2022/7/7. +// 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 "SASlinkCreator.h" +#import "SAReachability.h" +#import "SAJSONUtil.h" +#import "SensorsAnalyticsSDK+Private.h" +#import "SensorsAnalyticsSDK.h" +#import "SALog.h" +#import "SADeepLinkConstants.h" +#import "SAConstants+Private.h" + + +@implementation SATUTMProperties + +@end + +@interface SASlinkResponse () + +@property (nonatomic, assign) NSInteger statusCode; +@property (nonatomic, copy) NSString *message; +@property (nonatomic, copy, nullable) NSString *slink; +@property (nonatomic, copy) NSString *commonRedirectURI; + +- (instancetype)initWithSlink:(NSString *)slink slinkID:(NSString *)slinkID message:(NSString *)message statusCode:(NSInteger)statusCode commonRedirectURI:(NSString *)commonRedirectURI; + +@end + +@implementation SASlinkResponse + +- (instancetype)initWithSlink:(NSString *)slink slinkID:(NSString *)slinkID message:(NSString *)message statusCode:(NSInteger)statusCode commonRedirectURI:(NSString *)commonRedirectURI { + self = [super init]; + if (self) { + _slink = slink; + _slinkID = slinkID; + _message = message.length > 200 ? [message substringToIndex:200] : message; + _statusCode = statusCode; + _commonRedirectURI = commonRedirectURI; + } + return self; +} + +@end + +@interface SASlinkCreator () + +//required params +@property (nonatomic, copy) NSString *templateID; +@property (nonatomic, copy) NSString *channelName; +@property (nonatomic, copy) NSString *commonRedirectURI; +@property (nonatomic, copy) NSString *accessToken; + +@property (nonatomic, copy) NSString *channelType; +@property (nonatomic, copy) NSString *projectName; + + +@end + +@implementation SASlinkCreator + +- (instancetype)initWithTemplateID:(NSString *)templateID channelName:(NSString *)channelName commonRedirectURI:(NSString *)commonRedirectURI accessToken:(NSString *)accessToken { + self = [super init]; + if (self) { + _templateID = templateID; + _channelName = channelName; + _commonRedirectURI = commonRedirectURI; + _accessToken = accessToken; + _landingPageType = SATLandingPageTypeUndefined; + _channelType = @"app_share"; + } + return self; +} + +- (void)createSlinkWithCompletion:(void (^)(SASlinkResponse * _Nonnull))completion { + //check network reachable + if (![SAReachability sharedInstance].reachable) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoNetwork") statusCode:kSADynamicSlinkStatusCodeNoNetwork]; + completion(response); + return; + } + //check custom domain + NSString *customADChannelURL = SensorsAnalyticsSDK.sdkInstance.configOptions.customADChannelURL; + if (![customADChannelURL isKindOfClass:[NSString class]] || customADChannelURL.length < 1) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoDomain") statusCode:kSADynamicSlinkStatusCodeoNoDomain]; + completion(response); + return; + } + //check access token + if (![self.accessToken isKindOfClass:[NSString class]] || self.accessToken.length < 1) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoAccessToken") statusCode:kSADynamicSlinkStatusCodeLessParams]; + completion(response); + return; + } + //check project + NSString *project = SensorsAnalyticsSDK.sdkInstance.network.project; + if (![project isKindOfClass:[NSString class]] || project.length < 1) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoProject") statusCode:kSADynamicSlinkStatusCodeLessParams]; + completion(response); + return; + } + //check templateID + if (![self.templateID isKindOfClass:[NSString class]] || self.templateID.length < 1) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoTemplateID") statusCode:kSADynamicSlinkStatusCodeLessParams]; + completion(response); + return; + } + //check channel name + if (![self.channelName isKindOfClass:[NSString class]] || self.channelName.length < 1) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoChannelName") statusCode:kSADynamicSlinkStatusCodeLessParams]; + completion(response); + return; + } + //check commonRedirectURI + if (![self.commonRedirectURI isKindOfClass:[NSString class]] || self.commonRedirectURI.length < 1) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageNoRedirectURI") statusCode:kSADynamicSlinkStatusCodeLessParams]; + completion(response); + return; + } + + //request dynamic slink + NSDictionary *params = [self buildSlinkParams]; + NSURLRequest *request = [self buildSlinkRequestWithParams:params]; + NSURLSessionDataTask *task = [[SAHTTPSession sharedInstance] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable httpResponse, NSError * _Nullable error) { + if (!httpResponse) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:error.localizedDescription statusCode:error.code]; + completion(response); + return; + } + NSInteger statusCode = httpResponse.statusCode; + NSString *message = [NSHTTPURLResponse localizedStringForStatusCode:statusCode]; + NSDictionary *result = [SAJSONUtil JSONObjectWithData:data]; + if (![result isKindOfClass:[NSDictionary class]]) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:message statusCode:statusCode]; + completion(response); + return; + } + message = result[@"msg"] ? : message; + if (httpResponse.statusCode == 200) { + if (!result[@"code"]) { + SASlinkResponse *response = [self responseWithSlink:nil slinkID:nil message:SALocalizedString(@"SADynamicSlinkMessageResponseError") statusCode:kSADynamicSlinkStatusCodeResponseError]; + completion(response); + return; + } + statusCode = [result[@"code"] respondsToSelector:@selector(integerValue)] ? [result[@"code"] integerValue] : statusCode; + NSDictionary *slinkData = result[@"data"]; + NSString *slink = nil; + NSString *slinkID = nil; + if ([slinkData isKindOfClass:[NSDictionary class]]) { + slink = slinkData[@"short_url"]; + slinkID = slinkData[@"slink_id"]; + } + SASlinkResponse *response = [self responseWithSlink:slink slinkID:slinkID message:message statusCode:statusCode]; + completion(response); + return; + } + SASlinkResponse *slinkResponse = [self responseWithSlink:nil slinkID:nil message:message statusCode:statusCode]; + completion(slinkResponse); + }]; + [task resume]; +} + +- (SASlinkResponse *)responseWithSlink:(NSString *)slink slinkID:(NSString *)slinkID message:(NSString *)message statusCode:(NSInteger)statusCode { + SASlinkResponse *response = [[SASlinkResponse alloc] initWithSlink:slink slinkID:slinkID message:message statusCode:statusCode commonRedirectURI:self.commonRedirectURI]; + [self trackEventWithSlinkResponse:response]; + return response; +} + +- (void)trackEventWithSlinkResponse:(SASlinkResponse *)response { + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + properties[kSADynamicSlinkEventPropertyChannelType] = self.channelType; + properties[kSADynamicSlinkEventPropertyChannelName] = self.channelName ? : @""; + properties[kSADynamicSlinkEventPropertySource] = @"iOS"; + properties[kSADynamicSlinkEventPropertyData] = @""; + properties[kSADynamicSlinkEventPropertyShortURL] = response.slink ? : @""; + properties[kSADynamicSlinkEventPropertyStatus] = @(response.statusCode); + properties[kSADynamicSlinkEventPropertyMessage] = response.message; + properties[kSADynamicSlinkEventPropertyID] = response.slinkID ? : @""; + properties[kSADynamicSlinkEventPropertyTemplateID] = self.templateID ? : @""; + properties[kSADynamicSlinkEventPropertyType] = kSADynamicSlinkEventPropertyTypeDynamic; + [[SensorsAnalyticsSDK sharedInstance] track:kSADynamicSlinkEventName withProperties:[properties copy]]; +} + +- (NSDictionary *)buildSlinkParams { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + params[kSADynamicSlinkParamProject] = SensorsAnalyticsSDK.sdkInstance.network.project; + params[kSADynamicSlinkParamTemplateID] = self.templateID; + params[kSADynamicSlinkParamChannelType] = self.channelType; + params[kSADynamicSlinkParamChannelName] = self.channelName; + if (self.name) { + params[kSADynamicSlinkParamName] = self.name; + } + if (self.customParams) { + params[kSADynamicSlinkParamCustom] = self.customParams; + } + if (self.routeParam) { + params[kSADynamicSlinkParamRoute] = self.routeParam; + } + if (self.uriSchemeSuffix) { + params[kSADynamicSlinkParamURIScheme] = self.uriSchemeSuffix; + } + if (self.landingPageType == SATLandingPageTypeIntelligence) { + params[kSADynamicSlinkParamLandingPageType] = @"intelligence"; + } else if (self.landingPageType == SATLandingPageTypeOther) { + params[kSADynamicSlinkParamLandingPageType] = @"other"; + } else { + SALogInfo(@"Undefined Slink landing page type: %lu", self.landingPageType); + } + if (self.landingPage) { + params[kSADynamicSlinkParamLandingPage] = self.landingPage; + } + if (self.redirectURLOnOtherDevice) { + params[kSADynamicSlinkParamJumpAddress] = self.redirectURLOnOtherDevice; + } + if (!self.utmProperties) { + return [params copy]; + } + NSMutableDictionary *utmProperties = [NSMutableDictionary dictionary]; + if (self.utmProperties.source) { + utmProperties[kSADynamicSlinkParamUTMSource] = self.utmProperties.source; + } + if (self.utmProperties.campaign) { + utmProperties[kSADynamicSlinkParamUTMCampaign] = self.utmProperties.campaign; + } + if (self.utmProperties.medium) { + utmProperties[kSADynamicSlinkParamUTMMedium] = self.utmProperties.medium; + } + if (self.utmProperties.term) { + utmProperties[kSADynamicSlinkParamUTMTerm] = self.utmProperties.term; + } + if (self.utmProperties.content) { + utmProperties[kSADynamicSlinkParamUTMContent] = self.utmProperties.content; + } + params[kSADynamicSlinkParamFixedUTM] = [utmProperties copy]; + + return [params copy]; +} + +- (NSURLRequest *)buildSlinkRequestWithParams:(NSDictionary *)params { + NSString *customADChannelURL = SensorsAnalyticsSDK.sdkInstance.configOptions.customADChannelURL; + if (![customADChannelURL isKindOfClass:[NSString class]]) { + return nil; + } + NSURL *slinkBaseURL = [NSURL URLWithString:customADChannelURL]; + if (!slinkBaseURL) { + return nil; + } + NSURL *slinkURL = [slinkBaseURL URLByAppendingPathComponent:kSADynamicSlinkAPIPath]; + if (!slinkURL) { + return nil; + } + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:slinkURL]; + request.timeoutInterval = 30; + request.HTTPBody = [SAJSONUtil dataWithJSONObject:params]; + [request setHTTPMethod:@"POST"]; + [request setValue:self.accessToken forHTTPHeaderField:@"token"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + return request; +} + +@end diff --git a/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h b/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h index 9c8c2ce3..64107d07 100644 --- a/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h +++ b/SensorsAnalyticsSDK/Deeplink/SensorsAnalyticsSDK+Deeplink.h @@ -19,6 +19,7 @@ // #import "SensorsAnalyticsSDK.h" +#import "SASlinkCreator.h" NS_ASSUME_NONNULL_BEGIN diff --git a/SensorsAnalyticsSDK/SensorsAnalyticsSDK.bundle/zh-Hans.lproj/Localizable.strings b/SensorsAnalyticsSDK/SensorsAnalyticsSDK.bundle/zh-Hans.lproj/Localizable.strings index ef99acb4..5cc06460 100644 --- a/SensorsAnalyticsSDK/SensorsAnalyticsSDK.bundle/zh-Hans.lproj/Localizable.strings +++ b/SensorsAnalyticsSDK/SensorsAnalyticsSDK.bundle/zh-Hans.lproj/Localizable.strings @@ -93,3 +93,20 @@ "SAPresetPropertyCarrierTelecom" = "中国电信"; "SAPresetPropertyCarrierSatellite" = "中国卫通"; "SAPresetPropertyCarrierTietong" = "中国铁通"; + +//dynamic slink and device whitelist + +"SADeviceWhiteListTitle" = "添加设备白名单"; +"SADeviceWhiteListMessageProject" = "设备白名单配置项目与事件上报项目不一致"; +"SADeviceWhiteListMessageDeviceType" = "请使用 iOS 设备类型扫码"; +"SADeviceWhiteListMessageRequestSuccess" = "请求成功"; +"SADeviceWhiteListMessageRequestFailure" = "请求失败,请重新扫码"; + +"SADynamicSlinkMessageNoNetwork" = "没有检测到网络连接"; +"SADynamicSlinkMessageNoTemplateID" = "Slink 模板 ID 缺少"; +"SADynamicSlinkMessageNoChannelName" = "渠道名缺少"; +"SADynamicSlinkMessageNoAccessToken" = "token 缺少"; +"SADynamicSlinkMessageNoProject" = "获取不到数据埋点地址的项目名称"; +"SADynamicSlinkMessageNoRedirectURI" = "通用跳转链接缺少"; +"SADynamicSlinkMessageNoDomain" = "自定义域名缺少或者填写格式不对"; +"SADynamicSlinkMessageResponseError" = "后端返回数据异常";