IOS实现无埋点技术

技术方案实现的背景:

因为在开发项目的时候,埋点都是手动埋的,每次业务需求的改变都要到处埋点,这就免不了会遗漏埋点。每次在发版之前,大数据的人都会跑过来说某个点没有埋,哎,不仅开发效率变低,而且手动埋点真的好痛苦。在老大的提议下,决定学习一下无埋点技术,来解决我们现在的埋点的现状。经过几番查找资料,大致实施方案是采用运行时的方法交换来进行方法的替换,在需要埋点的地方先埋入点,然后再执行原方法,使得埋点从业务中抽离出来。

技术方案:(现在技术方案只是局限于不与业务逻辑挂钩的埋点,与业务挂钩的埋点还没有想到好的解决方案)

在项目中需要埋入的点大约分为两大类:

第一种:click事件

click事件中大致又分为button的点击事件,手势的点击事件,还有例如tableView的代理点击事件。首先对于button的点击事件,写一个button的分类,在分类中重写load方法,在load方法中进行方法的替换,然后替换button的点击事件-(void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents ,在替换之后的方法中进行埋点的处理,然后再调用原来的点击事件的方法。这样悄悄地埋入了埋点,又不改变之前代码的逻辑。因为UIButton是继承自UIControl的,所以也可以写UIControl的分类,凡是继承自UIControl的控件的响应事件最后都会走-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event此方法,可以将此方法进行替换。但是采用这种给控件写分类的模式不是太喜欢,我需要给项目中所有涉及到click事件的控件写分类,例如button,得写button的分类进行事件的替换,tableView的代理方法的替换得写tabView的分类,碍于这种繁琐,我的解决方案是写UIViewController的分类,给UIViewController中的ViewWillAppear方法进行替换,下面就是重点讲解方法的替换的实现啦,好啦,上代码来解释吧!

import "UIViewController+statistic.h"

import "LFDZHSwizzleTool.h"

import "LFDStatistic.h"

@implementation 

UIViewController (statistic)

+ (void)load{    

static dispatch_once_t dispatch_once_token;    dispatch_once(&dispatch_once_token, ^{      

  SEL originlSelector = @selector(viewDidAppear:);        

SEL newSelector = @selector(swizzleviewWillAppear:);    

//此处是我封装的方法交换的方法

    [LFDZHSwizzleTool zhSwizzleWithOrigionClass:[self class] originalSelector:originlSelector swizzleClass:[self class] swizzleSelector:newSelector];    });

}

- (void)swizzleviewWillAppear:(BOOL)animated{    

//加载plist    

NSString *pathString = [[NSBundle mainBundle]pathForResource:@"buttonStatistics" ofType:@"plist"];    

//获取加载plsit的字典    

NSDictionary *plistDic = [NSDictionary dictionaryWithContentsOfFile:pathString];  

  //获取当前类的方法列表    NSDictionary *classMthodDic = plistDic[NSStringFromClass([self class])];  

  //如果没有获取就返回    if (classMthodDic == nil) return; // 该类的方法不需要hook   

 //获取方法的所有的key值,放到数组当中    NSArray *methodNameArray = [classMthodDic allKeys];   

 //遍历所有的方法    

for (NSString *methodString in methodNameArray) {        

__block SEL originalSEL = NSSelectorFromString(methodString);       

 // 这里需要判断是否是UIControl的事件,以防止两个地方同时hook,造成重复上传       

 BOOL isResponseOriginSEL = [self respondsToSelector:originalSEL];      

  //如果该类中有此方法那么就替换该类中的方法        

if (isResponseOriginSEL) {           

//使用aspect替换类中的方法  ,aspect是一个封装好的用来交换方法的第三方的框架,可以去gitHub上去下载      

 [[self class] aspect_hookSelector:originalSEL withOptions:AspectPositionAfter usingBlock:^(idinfo){

NSString *selectorName = NSStringFromSelector(originalSEL);

NSString *pathString = [[NSBundle mainBundle]pathForResource:@"buttonStatistics" ofType:@"plist"];

NSDictionary *plistDic = [NSDictionary dictionaryWithContentsOfFile:pathString];

// 2.获取某个类的方法列表

NSDictionary *classMthodDic = plistDic[NSStringFromClass([self class])];

// 3.获取其中某个方法的参数列表

NSDictionary *parameterDic = classMthodDic[selectorName];

NSString *eventID = parameterDic[@"eventID"];

NSString *tarPageID = parameterDic[@"tarPageID"];

//在此处上报需要埋入的点

[LFDStatistic lfd_clickName:eventID tar_page_name:tarPageID params:nil];

}

error:nil];

}

}

[self swizzleviewWillAppear:animated];

}

plist表:

IOS实现无埋点技术_第1张图片

主要实现逻辑:给每个控制器的ViewWillAppear方法进行替换,在类中检索是否有plist表中需要埋入的点,如果有,就替换方法,在替换的方法中进行埋点。

第二种:expose事件

项目中因为所有的控制器都继承自一个baseVc,所以统一在baseVc的viewWillAppear方法中埋入expose事件。当然也可以在交换的方法中进行埋入。

以上只是解决了简单的不涉及业务的click事件,在接下来的日子里会继续跟踪更好的方法来解决这个问题。以上文章的实录要感谢来自上的简友。

埋点资料:https://github.com/Vienta/VIAnalyticsKit/blob/master/VIAnalyticsKit/Class/VIAnalyticsAOP.m

你可能感兴趣的:(IOS实现无埋点技术)