『ios』无痕埋点探索 (页面显示 UIButton UIGes
2021-06-15 本文已影响0人
butterflyer
这是一篇无痕埋点方案的探索,学到的东西,怕忘记了,所以记下来,就这样。
我们在平时的项目中,会用到埋点这个功能吧,如果页面很少,还好说,我们可以手动进行埋点,如果是页面变多,那么手动埋点将会变得非常的浪费时间和效率,所以无痕埋点就这样诞生了。
讲无痕埋点之前先放一个方法。
交换方法
+(void)swizzingForClass:(Class)cls originalSel:(SEL)originalSelector swizzingSel:(SEL)swizzingSelector
{
Class class = cls;
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzingMethod = class_getInstanceMethod(class, swizzingSelector);
BOOL addMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzingMethod),
method_getTypeEncoding(swizzingMethod));
if (addMethod) {
class_replaceMethod(class,
swizzingSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzingMethod);
}
}
因为埋点的精髓部分在于对数据的处理,所以别急,继续往下看。
对于页面显示隐藏方面的埋点该怎么做呢?没错就是对viewWillAppear viewWillDisappear viewDidLoad进行交换,然后监听。
+(void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalAppearSelector = @selector(viewWillAppear:);
SEL swizzingAppearSelector = @selector(user_viewWillAppear:);
[MethodSwizzingTool swizzingForClass:[self class] originalSel:originalAppearSelector swizzingSel:swizzingAppearSelector];
SEL originalDisappearSelector = @selector(viewWillDisappear:);
SEL swizzingDisappearSelector = @selector(user_viewWillDisappear:);
[MethodSwizzingTool swizzingForClass:[self class] originalSel:originalDisappearSelector swizzingSel:swizzingDisappearSelector];
SEL originalDidLoadSelector = @selector(viewDidLoad);
SEL swizzingDidLoadSelector = @selector(user_viewDidLoad);
[MethodSwizzingTool swizzingForClass:[self class] originalSel:originalDidLoadSelector swizzingSel:swizzingDidLoadSelector];
});
}
那对于按钮的点击监听呢?别忘了这个方法sendAction:to:forEvent:。
SEL originalSelector = @selector(sendAction:to:forEvent:);
SEL swizzingSelector = @selector(user_sendAction:to:forEvent:);
[MethodSwizzingTool swizzingForClass:[self class] originalSel:originalSelector swizzingSel:swizzingSelector];
那如何定位到到点击了哪个btn呢?先看数据结构吧
"ACTION": {
"ViewController/jumpSecond/0": {
"userDefined": {
"eventid": "201803074|93",
"target": "",
"pageid": "234",
"pagename": "button点击,跳转至下一个页面"
},
"pagePara": {
"testKey9": {
"propertyName": "testPara",
"propertyPath":"",
"containIn": "0"
}
}
},
"SecondViewController/back": {
"userDefined": {
"eventid": "201803074|965",
"target": "second",
"pageid": "234",
"pagename": "button点击,返回"
},
"pagePara": {
"testKey9": {
"propertyName": "testPara",
"propertyPath":"",
"containIn": "0"
}
}
}
},
再来看监听的事件
-(void)user_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
[self user_sendAction:action to:target forEvent:event];
NSString * identifier = [NSString stringWithFormat:@"%@/%@/%ld", [target class], NSStringFromSelector(action),self.tag];
NSDictionary * dic = [[[DataContainer dataInstance].data objectForKey:@"ACTION"] objectForKey:identifier];
if (dic) {
NSString * eventid = dic[@"userDefined"][@"eventid"];
NSString * targetname = dic[@"userDefined"][@"target"];
NSString * pageid = dic[@"userDefined"][@"pageid"];
NSString * pagename = dic[@"userDefined"][@"pagename"];
NSDictionary * pagePara = dic[@"pagePara"];
__block NSMutableDictionary * uploadDic = [NSMutableDictionary dictionaryWithCapacity:0];
[pagePara enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id value = [CaptureTool captureVarforInstance:target withPara:obj];
if (value && key) {
[uploadDic setObject:value forKey:key];
}
}];
NSLog(@"\n event id === %@,\n target === %@, \n pageid === %@,\n pagename === %@,\n pagepara === %@ \n", eventid, targetname, pageid, pagename, uploadDic);
}
}
可以看到identifier @"ViewController/jumpSecond/0"
我们根据当前所在的类 跳转的方法 按钮的tag 来找到那个btn,进行进行打点。
![](https://img.haomeiwen.com/i2440780/bf399806357b34af.png)
对于手势的监听呢?这个跟btn这些就不太一样了。这个需要监听设置代理的方法。initWithTarget:action:
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[MethodSwizzingTool swizzingForClass:[self class] originalSel:@selector(initWithTarget:action:) swizzingSel:@selector(vi_initWithTarget:action:)];
});
}
然后添加关于sel_ SEL "UIDimmingView/handleSingleTap:" 的方法实现responseUser_gesture,具体UIDimmingView是什么可以自行打印查看。然后与handleSingleTap进行方法交换。
- (instancetype)vi_initWithTarget:(nullable id)target action:(nullable SEL)action
{
UIGestureRecognizer *selfGestureRecognizer = [self vi_initWithTarget:target action:action];
if (!target && !action) {
return selfGestureRecognizer;
}
if ([target isKindOfClass:[UIScrollView class]]) {
return selfGestureRecognizer;
}
Class class = [target class];
SEL sel = action;
NSString * sel_name = [NSString stringWithFormat:@"%s/%@", class_getName([target class]),NSStringFromSelector(action)];
SEL sel_ = NSSelectorFromString(sel_name);
BOOL isAddMethod = class_addMethod(class,
sel_,
method_getImplementation(class_getInstanceMethod([self class], @selector(responseUser_gesture:))),
nil);
//看到这里别疑惑,这里是我们给UIGestureRecognizer添加的属性。
self.methodName = NSStringFromSelector(action);
if (isAddMethod) {
Method selMethod = class_getInstanceMethod(class, sel);
Method sel_Method = class_getInstanceMethod(class, sel_);
method_exchangeImplementations(selMethod, sel_Method);
}
return selfGestureRecognizer;
}
然后我们可以实现responseUser_gesture
-(void)responseUser_gesture:(UIGestureRecognizer *)gesture
{
NSString * identifier = [NSString stringWithFormat:@"%s/%@", class_getName([self class]),gesture.methodName];
SEL sel = NSSelectorFromString(identifier);
if ([self respondsToSelector:sel]) {
IMP imp = [self methodForSelector:sel];
void (*func)(id, SEL,id) = (void *)imp;
func(self, sel,gesture);
}
NSDictionary * dic = [[[DataContainer dataInstance].data objectForKey:@"GESTURE"] objectForKey:identifier];
if (dic) {
NSString * eventid = dic[@"userDefined"][@"eventid"];
NSString * targetname = dic[@"userDefined"][@"target"];
NSString * pageid = dic[@"userDefined"][@"pageid"];
NSString * pagename = dic[@"userDefined"][@"pagename"];
NSDictionary * pagePara = dic[@"pagePara"];
__block NSMutableDictionary * uploadDic = [NSMutableDictionary dictionaryWithCapacity:0];
[pagePara enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id value = [CaptureTool captureVarforInstance:self withPara:obj];
if (value && key) {
[uploadDic setObject:value forKey:key];
}
}];
NSLog(@"\n event id === %@,\n target === %@, \n pageid === %@,\n pagename === %@,\n pagepara === %@ \n", eventid, targetname, pageid, pagename, uploadDic);
}
}
我们可以根据手势所在的类名和方法来确定这是哪一个手势。当然如果你可以保证手势名字的唯一性。直接用名字也可以。
![](https://img.haomeiwen.com/i2440780/c05062f58d8119d9.png)
"GESTURE": {
"ViewController/gesture1clicked:":{
"userDefined": {
"eventid": "201803074|93",
"target": "",
"pageid": "手势1对应的id",
"pagename": "手势1对应的page name"
},
"pagePara": {
"testKey1": {
"propertyName": "testPara",
"propertyPath":"",
"containIn": "0"
}
}
},
"ViewController/gesture2clicked:":{
"userDefined": {
"eventid": "201803074|93",
"target": "",
"pageid": "手势2对应的id",
"pagename": "手势2对应的page name"
},
"pagePara": {
"testKey2": {
"propertyName": "testPara",
"propertyPath":"",
"containIn": "0"
}
}
},
"SecondViewController/gesture3clicked:":{
"userDefined": {
"eventid": "201803074|98",
"target": "",
"pageid": "gesture3clicked",
"pagename": "手势3对应的page name"
},
"pagePara": {
"user_age": {
}
}
}
}
对于埋点配置参数方面内容,另开一篇来说。因为还涉及到tableView和collectionView.
站在巨人的肩膀上才能看的更远,不断学习才能成长的更快。