diff --git a/SensorsAnalyticsSDK.podspec b/SensorsAnalyticsSDK.podspec index e4f0d990..c88edce8 100644 --- a/SensorsAnalyticsSDK.podspec +++ b/SensorsAnalyticsSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SensorsAnalyticsSDK" - s.version = "2.5.0" + s.version = "2.5.1" 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}" } @@ -17,12 +17,19 @@ Pod::Spec.new do |s| c.public_header_files = core_dir + "SensorsAnalyticsSDK.h", core_dir + "SensorsAnalyticsSDK+Public.h", core_dir + "SAAppExtensionDataManager.h", core_dir + "SASecurityPolicy.h", core_dir + "SAConfigOptions.h", core_dir + "SAConstants.h" c.resource = 'SensorsAnalyticsSDK/SensorsAnalyticsSDK.bundle' end - + s.subspec 'Core' do |c| c.dependency 'SensorsAnalyticsSDK/Common' c.dependency 'SensorsAnalyticsSDK/Gesture' end + # 支持 CAID 渠道匹配 + s.subspec 'CAID' do |f| + f.dependency 'SensorsAnalyticsSDK/Core' + f.source_files = "SensorsAnalyticsSDK/CAID/**/*.{h,m}" + f.private_header_files = 'SensorsAnalyticsSDK/CAID/**/*.h' + end + # 手势采集 s.subspec 'Gesture' do |g| g.dependency 'SensorsAnalyticsSDK/Common' diff --git a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj index 6a1f1de5..4bf3ed7d 100644 --- a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj +++ b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj @@ -220,6 +220,8 @@ FC23719523D0519A00DDD708 /* SALinkHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC23719423D0519A00DDD708 /* SALinkHandlerTests.m */; }; FC3C58DA23BEEFB000D83BF4 /* SATrackTimerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC3C58D923BEEFB000D83BF4 /* SATrackTimerTests.m */; }; FC80AB7C242CB17A006D1D25 /* SAIdentifierTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC80AB7B242CB17A006D1D25 /* SAIdentifierTests.m */; }; + FCEA7FBA25F8D95400C81687 /* SACAIDUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FCEA7FB825F8D95400C81687 /* SACAIDUtils.m */; }; + FCEA7FBB25F8D95400C81687 /* SACAIDUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FCEA7FB925F8D95400C81687 /* SACAIDUtils.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -469,6 +471,8 @@ FC23719423D0519A00DDD708 /* SALinkHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SALinkHandlerTests.m; sourceTree = ""; }; FC3C58D923BEEFB000D83BF4 /* SATrackTimerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SATrackTimerTests.m; sourceTree = ""; }; FC80AB7B242CB17A006D1D25 /* SAIdentifierTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SAIdentifierTests.m; sourceTree = ""; }; + FCEA7FB825F8D95400C81687 /* SACAIDUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SACAIDUtils.m; sourceTree = ""; }; + FCEA7FB925F8D95400C81687 /* SACAIDUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SACAIDUtils.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -898,6 +902,7 @@ children = ( F277F5B025CF9A43009B5CE6 /* AppPush */, 881A40D2253D7B4F00854F69 /* Core */, + FCEA7FB725F8D95400C81687 /* CAID */, 881A4224253D7B5E00854F69 /* Location */, 453FC62425EE3D6300591C47 /* Gesture */, CB30C0AB22840F1B0004061D /* Resources */, @@ -951,6 +956,15 @@ path = Resources; sourceTree = ""; }; + FCEA7FB725F8D95400C81687 /* CAID */ = { + isa = PBXGroup; + children = ( + FCEA7FB925F8D95400C81687 /* SACAIDUtils.h */, + FCEA7FB825F8D95400C81687 /* SACAIDUtils.m */, + ); + path = CAID; + sourceTree = ""; + }; F2346B7D25E0AE5600AA3684 /* HookDelegate */ = { isa = PBXGroup; children = ( @@ -1088,6 +1102,7 @@ 4D4DB2E725B7D1F300938842 /* SAReferrerManager.h in Headers */, 4D9E5DC925943BCE0095A0D0 /* SAApplicationStateSerializer.h in Headers */, 881A419B253D7B4F00854F69 /* SAConfigOptions.h in Headers */, + FCEA7FBB25F8D95400C81687 /* SACAIDUtils.h in Headers */, 4D9E5DC325943BCE0095A0D0 /* SATypeDescription.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1317,6 +1332,7 @@ 4D9E5DA925943BCD0095A0D0 /* SAVisualizedConnection.m in Sources */, A8A87E4D2568EBD500E16895 /* SARemoteConfigManager.m in Sources */, 4D9E5DBA25943BCE0095A0D0 /* SAJSTouchEventView.m in Sources */, + FCEA7FBA25F8D95400C81687 /* SACAIDUtils.m in Sources */, 4D9E5DC825943BCE0095A0D0 /* SAValueTransformers.m in Sources */, 881A4210253D7B5000854F69 /* SAIdentifier.m in Sources */, 4D9E5DC725943BCE0095A0D0 /* SAVisualizedUtils.m in Sources */, diff --git a/SensorsAnalyticsSDK/CAID/SACAIDUtils.h b/SensorsAnalyticsSDK/CAID/SACAIDUtils.h new file mode 100644 index 00000000..4d980cdc --- /dev/null +++ b/SensorsAnalyticsSDK/CAID/SACAIDUtils.h @@ -0,0 +1,29 @@ +// +// SACAIDUtils.h +// 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. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SACAIDUtils : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/CAID/SACAIDUtils.m b/SensorsAnalyticsSDK/CAID/SACAIDUtils.m new file mode 100644 index 00000000..9ed55d9f --- /dev/null +++ b/SensorsAnalyticsSDK/CAID/SACAIDUtils.m @@ -0,0 +1,96 @@ +// +// 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 "SAFileStore.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 = [SAFileStore unarchiveWithFileName: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 信息 + [SAFileStore archiveWithFileName:kSACAIDCacheKey value:caid]; + } + 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/Core/Builder/SAIdentifier.h b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h index c030274f..6ccd5dd0 100644 --- a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h +++ b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.h @@ -82,6 +82,13 @@ NS_ASSUME_NONNULL_BEGIN */ + (NSString *)idfa; +/** + 获取设备的 IDFV + + @return idfv + */ ++ (NSString *)idfv; + /** 生成匿名 Id(设备 Id):IDFA -> IDFV -> UUID diff --git a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m index 00e9a0d7..c0ce4eb7 100644 --- a/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m +++ b/SensorsAnalyticsSDK/Core/Builder/SAIdentifier.m @@ -139,12 +139,16 @@ + (NSString *)idfa { return nil; } ++ (NSString *)idfv { + return [UIDevice currentDevice].identifierForVendor.UUIDString;; +} + + (NSString *)uniqueHardwareId { NSString *distinctId = [self idfa]; // 没有IDFA,则使用IDFV if (!distinctId) { - distinctId = [UIDevice currentDevice].identifierForVendor.UUIDString; + distinctId = [self idfv]; } // 没有IDFV,则使用UUID diff --git a/SensorsAnalyticsSDK/Core/Channel/SAChannelMatchManager.m b/SensorsAnalyticsSDK/Core/Channel/SAChannelMatchManager.m index 1e9dbbeb..88850512 100644 --- a/SensorsAnalyticsSDK/Core/Channel/SAChannelMatchManager.m +++ b/SensorsAnalyticsSDK/Core/Channel/SAChannelMatchManager.m @@ -32,7 +32,7 @@ #import "SAReachability.h" #import "SALog.h" -NSString * const SAChannelDebugFlagKey = @"com.sensorsdata.channeldebug.flag"; +NSString * const SAIsValidForChannelDebugKey = @"com.sensorsdata.channeldebug.flag"; NSString * const SAChannelDebugInstallEventName = @"$ChannelDebugInstall"; @interface SAChannelMatchManager () @@ -82,32 +82,42 @@ - (UIWindow *)alertWindow { } #pragma mark - 渠道联调诊断标记 -// 客户是否手动触发过激活事件 -- (BOOL)isAppInstall { - NSNumber *appInstalled = [[NSUserDefaults standardUserDefaults] objectForKey:SAChannelDebugFlagKey]; - return (appInstalled != nil); +/// 客户是否触发过激活事件 +- (BOOL)isAppInstalled { + NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults]; + return [userDefault boolForKey:SA_HAS_TRACK_INSTALLATION_DISABLE_CALLBACK] || [userDefault boolForKey:SA_HAS_TRACK_INSTALLATION]; } -// 客户手动触发过的激活事件中 IDFA 是否为空 -- (BOOL)isNotEmptyIDFAOfAppInstall { - return [[NSUserDefaults standardUserDefaults] boolForKey:SAChannelDebugFlagKey]; +/// 客户可以使用渠道联调诊断功能 +- (BOOL)isValidForChannelDebug { + if (![self isAppInstalled]) { + // 当未触发过激活事件时,可以使用联调诊断功能 + return YES; + } + return [[NSUserDefaults standardUserDefaults] boolForKey:SAIsValidForChannelDebugKey]; +} + +/// 当前获取到的设备 ID 为有效值 +- (BOOL)isValidOfDeviceInfo { + return ([SAIdentifier idfa].length > 0 || [self CAIDInfo].allKeys > 0); } #pragma mark - 激活事件 - (void)trackAppInstall:(NSString *)event properties:(NSDictionary *)properties disableCallback:(BOOL)disableCallback { + NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults]; NSString *userDefaultsKey = disableCallback ? SA_HAS_TRACK_INSTALLATION_DISABLE_CALLBACK : SA_HAS_TRACK_INSTALLATION; - BOOL hasTrackInstallation = [[NSUserDefaults standardUserDefaults] boolForKey:userDefaultsKey]; + BOOL hasTrackInstallation = [userDefault boolForKey:userDefaultsKey]; if (hasTrackInstallation) { return; } - // 渠道联调诊断功能 - 激活事件中 IDFA 内容是否为空 - BOOL isNotEmpty = [SAIdentifier idfa].length > 0; - [[NSUserDefaults standardUserDefaults] setValue:@(isNotEmpty) forKey:SAChannelDebugFlagKey]; + + // 记录激活事件是否获取到了有效的设备 ID 信息,设备 ID 信息有效时后续可以使用联调诊断功能 + [userDefault setBool:[self isValidOfDeviceInfo] forKey:SAIsValidForChannelDebugKey]; // 激活事件 - 根据 disableCallback 记录是否触发过激活事件 - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:userDefaultsKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [userDefault setBool:YES forKey:userDefaultsKey]; + [userDefault synchronize]; NSMutableDictionary *eventProps = [NSMutableDictionary dictionary]; if ([SAValidator isValidDictionary:properties]) { @@ -125,9 +135,7 @@ - (void)trackChannelDebugInstallEvent { - (void)handleAppInstallEvent:(NSString *)event properties:(NSDictionary *)properties { NSMutableDictionary *eventProps = [NSMutableDictionary dictionaryWithDictionary:properties]; - NSString *idfa = [SAIdentifier idfa]; - NSString *appInstallSource = idfa ? [NSString stringWithFormat:@"idfa=%@", idfa] : @""; - eventProps[SA_EVENT_PROPERTY_APP_INSTALL_SOURCE] = appInstallSource; + eventProps[SA_EVENT_PROPERTY_APP_INSTALL_SOURCE] = [self appInstallSource]; NSString *userAgent = eventProps[SA_EVENT_PROPERTY_APP_USER_AGENT]; if (userAgent.length == 0) { @@ -140,6 +148,27 @@ - (void)handleAppInstallEvent:(NSString *)event properties:(NSDictionary *)prope } } +- (NSString *)appInstallSource { + NSMutableDictionary *sources = [NSMutableDictionary dictionary]; + [sources addEntriesFromDictionary:[self CAIDInfo]]; + 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:@"##"]; +} + +- (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; +} + - (void)trackAppInstallEvent:(NSString *)event properties:(NSDictionary *)properties { // 先发送 track SensorsAnalyticsSDK *sdk = [SensorsAnalyticsSDK sharedInstance]; @@ -200,8 +229,16 @@ - (BOOL)isRelinkURL:(NSURL *)url { - (void)showRelinkAlertWithURL:(NSURL *)url { NSDictionary *queryItems = [SAURLUtils queryItemsWithURL:url]; - NSString *deviceId = queryItems[@"device_code"]; - if ([deviceId isEqualToString:[SAIdentifier idfa]]) { + NSString *deviceId = [queryItems[@"device_code"] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + // 重连二维码对应的设备信息 + 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) { [self showChannelDebugInstall]; } else { [self showErrorMessage:@"无法重连,请检查是否更换了联调手机"]; @@ -213,11 +250,12 @@ - (void)showAuthorizationAlertWithURL:(NSURL *)url { SAAlertController *alertController = [[SAAlertController alloc] initWithTitle:@"即将开启联调模式" message:nil preferredStyle:SAAlertControllerStyleAlert]; __weak SAChannelMatchManager *weakSelf = self; [alertController addActionWithTitle:@"确认" style:SAAlertActionStyleDefault handler:^(SAAlertAction * _Nonnull action) { - if (![weakSelf isAppInstall] || ([weakSelf isNotEmptyIDFAOfAppInstall] && [SAIdentifier idfa])) { + __strong SAChannelMatchManager *strongSelf = weakSelf; + if ([strongSelf isValidForChannelDebug] && [strongSelf isValidOfDeviceInfo]) { NSDictionary *qureyItems = [SAURLUtils queryItemsWithURL:url]; - [weakSelf uploadUserInfoIntoWhiteList:qureyItems]; + [strongSelf uploadUserInfoIntoWhiteList:qureyItems]; } else { - [weakSelf showChannelDebugErrorMessage]; + [strongSelf showChannelDebugErrorMessage]; } }]; [alertController addActionWithTitle:@"取消" style:SAAlertActionStyleCancel handler:nil]; @@ -242,8 +280,8 @@ - (void)uploadUserInfoIntoWhiteList:(NSDictionary *)qureyItems { NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:qureyItems]; params[@"distinct_id"] = [[SensorsAnalyticsSDK sharedInstance] distinctId]; - params[@"has_active"] = @([self isAppInstall]); - params[@"device_code"] = [SAIdentifier idfa]; + params[@"has_active"] = @([self isAppInstalled]); + params[@"device_code"] = [self appInstallSource]; request.HTTPBody = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil]; [self showIndicator]; @@ -288,7 +326,7 @@ - (void)showChannelDebugInstall { #pragma mark - Error Message - (void)showChannelDebugErrorMessage { NSString *title = @"检测到“设备码为空”,可能的原因如下,请排查:"; - NSString *content = @"\n1. 手机系统设置中「隐私->广告-> 限制广告追踪」;\n\n2.若手机系统为 iOS 14 ,请联系研发人员确认 trackInstallation 接口是否在 “跟踪” 授权之后调用。\n\n排查修复后,请重新扫码进行联调。"; + NSString *content = @"\n1. 手机系统设置中「隐私->广告-> 限制广告追踪」;\n\n2.若手机系统为 iOS 14 ,请联系研发人员确认 trackAppInstall 接口是否在 “跟踪” 授权之后调用。\n\n排查修复后,请重新扫码进行联调。\n\n3. 若集成了 CAID SDK,请联系研发人员确认 trackAppInstall 接口是否在 “getCAIDAsyncly” 授权之后调用。\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/SASwizzle.h b/SensorsAnalyticsSDK/Core/SASwizzle.h index 1e619fbc..2d3bc342 100755 --- a/SensorsAnalyticsSDK/Core/SASwizzle.h +++ b/SensorsAnalyticsSDK/Core/SASwizzle.h @@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN + (BOOL)sa_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError **)error_; + (BOOL)sa_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError **)error_; ++ (BOOL)sa_swizzleMethod:(SEL)origSel_ withClass:(Class)altCla_ withMethod:(SEL)altSel_ error:(NSError **)error_; + @end NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Core/SASwizzle.m b/SensorsAnalyticsSDK/Core/SASwizzle.m index b4b032e1..6d3627b0 100755 --- a/SensorsAnalyticsSDK/Core/SASwizzle.m +++ b/SensorsAnalyticsSDK/Core/SASwizzle.m @@ -48,6 +48,47 @@ @implementation NSObject (SASwizzle) ++ (BOOL)sa_swizzleMethod:(SEL)origSel_ withClass:(Class)altCla_ withMethod:(SEL)altSel_ error:(NSError **)error_ { + Method origMethod = class_getInstanceMethod(self, origSel_); + if (!origMethod) { +#if TARGET_OS_IPHONE + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); +#else + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); +#endif + return NO; + } + + Method altMethod = class_getInstanceMethod(altCla_, altSel_); + if (!altMethod) { +#if TARGET_OS_IPHONE + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [altCla_ class]); +#else + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [altCla_ className]); +#endif + return NO; + } + + class_addMethod(self, + origSel_, + class_getMethodImplementation(self, origSel_), + method_getTypeEncoding(origMethod)); + class_addMethod(altCla_, + altSel_, + class_getMethodImplementation(altCla_, altSel_), + method_getTypeEncoding(altMethod)); + + //交换之前,先对自定义方法进行添加 + BOOL didAddMethod = class_addMethod(self, + altSel_, + method_getImplementation(altMethod), + method_getTypeEncoding(altMethod)); + if (didAddMethod) { + method_exchangeImplementations(origMethod, class_getInstanceMethod(self, altSel_)); + } + return didAddMethod; +} + + (BOOL)sa_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError **)error_ { #if OBJC_API_VERSION >= 2 Method origMethod = class_getInstanceMethod(self, origSel_); diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m index 639ded02..21f6d655 100755 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m @@ -79,7 +79,7 @@ #import "SAChannelMatchManager.h" #import "SAReferrerManager.h" -#define VERSION @"2.5.0" +#define VERSION @"2.5.1" static NSUInteger const SA_PROPERTY_LENGTH_LIMITATION = 8191;