用戶行為統計,俗稱埋點,是一個成熟項目中必不可少的環節。埋點的常規做法是在項目中所有需要埋點的地方插入埋點,但隨著項目不斷壯大,埋點的地方越來越多,埋點代碼散落在項目中不同角落,不易于管理和后期維護,出于簡化埋點開發的目的,針對自動埋點做了小小的總結。
小豆暫且把埋點分為兩種:
1>頁面統計,即在進入頁面和離開頁面的時候埋點,統計停留頁面時長
2>交互事件統計
本文暫且以按鈕點擊事件埋點
為例來簡單地講述自動埋點的思路。
常規埋點
常規的點擊事件埋點,大概是醬紫:
- (void)totalBillAction:(UIButton *)sender
{
[Agent useCustomizeEvent:@"loan114" extra:nil];
}
常規埋點簡單直接,哪里需要埋哪里,so easy!但如此一來,代碼的復用性幾乎為0,維護性也并不理想。那么,我們來借助一下“黑魔法”來實現簡易的自動埋點。
自動埋點
1.技術原理:Method-Swizzling
Method-Swizzling,俗稱RunTime的“黑魔法”,屬于面向切面編程的一種實現。具體操作是在重載類的load
方法中,通過method_exchangeImplementations
等接口實現方法交換,讓程序執行我們的方法。
方法交換的代碼,大概是醬紫:
+ (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector
{
Class class = cls;
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod)
{
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
}
else
{
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
2.實現思路
對于一個給定的事件,UIControl會調用sendAction:to:forEvent:
來將行為消息轉發到UIApplication對象,再由UIApplication對象調用其sendAction:to:fromSender:forEvent:
方法來將消息分發到指定的target上,那么,我們寫一個UIControl的類別,通過替換它的sendAction:to:forEvent:
方法,結合本地配置的埋點json或者plist文件(若埋點需要額外的參數,需要給UIControl的類別通過Runtime添加屬性),便可以實現自動埋點的功能。
具體實現如下:
@implementation UIControl (UserStastistics)
static char *extraKey = "stastisticExtraKey";
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
//原方法
SEL originalSelector = @selector(sendAction:to:forEvent:);
//我們要實現的方法
SEL swizzledSelector = @selector(swiz_sendAction:to:forEvent:);
//方法交換(具體實現在上面)
[CommonUtility swizzlingInClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector];
});
}
#pragma mark - Runtime增加屬性
- (void)setStastisticExtraDic:(NSMutableDictionary *)stastisticExtraDic
{
objc_setAssociatedObject(self, &extraKey,stastisticExtraDic,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)stastisticExtraDic
{
return objc_getAssociatedObject(self, &extraKey);
}
#pragma mark - Method Swizzling
- (void)swiz_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
[self swiz_sendAction:action to:target forEvent:event];
//插入埋點代碼
[self performUserStastisticsAction:action to:target forEvent:event];
}
- (void)performUserStastisticsAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
NSString *actionStr = NSStringFromSelector(action);
actionStr = [actionStr hasPrefix:@"_"]?[actionStr substringFromIndex:1]:actionStr;
//我以NSStringFromClass([target class])_actionStr_self.tag作為key來配置埋點文件
NSString *controlName = self.tag>0?[NSString stringWithFormat:@"%@_%@_%ld", NSStringFromClass([target class]), actionStr,self.tag]:[NSString stringWithFormat:@"%@_%@", NSStringFromClass([target class]), actionStr];
//埋點具體實現
[StastisticsUtility stastisticEventData:controlName extraDic:self.stastisticExtraDic];
}
埋點的配置文件,大概是醬紫:
{
"eventStastistics":
{
"RegistrationView_nextBtnClick:":
{
"eventId":"sxj_01_004",
"eventName":"登錄/注冊-點擊【下一步】按鈕"
},
"VerificationCodeView_voiceSmsButtonClick:":
{
"eventId":"sxj_01_008",
"eventName":"登錄/注冊-點擊【收不到短信,試試語音驗證碼】按鈕"
},
"manual_LoginView_popUp":
{
"eventId":"sxj_01_009",
"eventName":"登錄/注冊-進入彈窗"
},
}
}
為了集中管理,我的所有埋點配置都寫在了這個json文件中,不方便寫自動埋點的,我會以manual為開頭命名它的key,在該埋點的地方如下實現:
- (void)totalBillAction:(UIButton *)sender
{
[StastisticsUtility stastisticEventData:@"manual_LoginView_popUp" extraDic:nil];
}
到這里,簡易的自動埋點功能已經實現,頁面進出的埋點思路類似,Demo是木有的,因為懶啊!