項目中需要統(tǒng)計的數(shù)據(jù)包括
1.在某個頁面的停留時間(針對UIViewController)
2.某個事件(method)觸發(fā)的次數(shù)
3.某個View的展示次數(shù)
(目前屬于試驗階段)
1.停留時間runtime swap viewDidAppear & viewDidDisAppear
#import "UIViewController+JHswizzling.h"
static NSDate *startDate;
@implementation UIViewController (JHswizzling)
+ (void)load{
// 通過class_getInstanceMethod()函數(shù)從當(dāng)前對象中的method list獲取method結(jié)構(gòu)體呻待,如果是類方法就使用class_getClassMethod()函數(shù)獲取庸诱。
Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidAppear:));
Method toMethod = class_getInstanceMethod([self class], @selector(JH_swizzlingViewDidAppear));
/**
* 我們在這里使用class_addMethod()函數(shù)對Method Swizzling做了一層驗證,如果self沒有實現(xiàn)被交換的方法斟湃,會導(dǎo)致失敗熏迹。
* 而且self沒有交換的方法實現(xiàn),但是父類有這個方法凝赛,這樣就會調(diào)用父類的方法注暗,結(jié)果就不是我們想要的結(jié)果了坛缕。
* 所以我們在這里通過class_addMethod()的驗證,如果self實現(xiàn)了這個方法捆昏,class_addMethod()函數(shù)將會返回NO赚楚,我們就可以對其進(jìn)行交換了。
*/
if (!class_addMethod([self class], @selector(JH_swizzlingViewDidAppear), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
method_exchangeImplementations(fromMethod, toMethod);
}
// 通過class_getInstanceMethod()函數(shù)從當(dāng)前對象中的method list獲取method結(jié)構(gòu)體骗卜,如果是類方法就使用class_getClassMethod()函數(shù)獲取宠页。
Method fromMethodDis = class_getInstanceMethod([self class], @selector(viewDidDisappear:));
Method toMethodDis = class_getInstanceMethod([self class], @selector(JH_swizzlingViewDidDisAppear));
if (!class_addMethod([self class], @selector(JH_swizzlingViewDidDisAppear), method_getImplementation(toMethodDis), method_getTypeEncoding(toMethodDis))) {
method_exchangeImplementations(fromMethodDis, toMethodDis);
}
}
// 我們自己實現(xiàn)的方法,也就是和self的viewDidAppear方法進(jìn)行交換的方法寇仓。
- (void)JH_swizzlingViewDidAppear{
NSString *str = [NSString stringWithFormat:@"%@", self.class];
// 我們在這里加一個判斷举户,將系統(tǒng)的UIViewController的對象剔除掉
NSDictionary *data = [self getJsonData];
if ([[data allKeys]containsObject:str]) {
if(![str containsString:@"UI"]){
startDate = [NSDate date];
NSLog(@"統(tǒng)計打點出現(xiàn) : %@ time : %@", [self getJsonData][str] ,startDate);
}
}
[self JH_swizzlingViewDidAppear];
}
// 我們自己實現(xiàn)的方法,也就是和self的viewDidDisAppear方法進(jìn)行交換的方法焚刺。
- (void)JH_swizzlingViewDidDisAppear{
NSString *str = [NSString stringWithFormat:@"%@", self.class];
// 我們在這里加一個判斷敛摘,將系統(tǒng)的UIViewController的對象剔除掉
NSDictionary *data = [self getJsonData];
if ([[data allKeys]containsObject:str]) {
if(![str containsString:@"UI"]){
//計算時間差
NSDate *endDate = [NSDate date];
NSTimeInterval duration = [endDate timeIntervalSinceDate:startDate];
NSLog(@"統(tǒng)計打點出現(xiàn) : %@ time : %f 時長", data[str] ,duration);
//組合數(shù)據(jù)并存入數(shù)據(jù)庫
NSDictionary *vcDic = @{@"viewControllerCodeName":str,
@"viewControllerName":data[str],
@"viewControllerTime":[NSString stringWithFormat:@"%f",duration],
};
[JH_AnalyseDataHelper _AnalyseWithData:vcDic withType:AnalyseTypeViewController];
}
}
[self JH_swizzlingViewDidDisAppear];
}
-(NSDictionary *)getJsonData{
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"analyse" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
return dic[@"viewController"];
}
2.某個事件(method)觸發(fā)的次數(shù),針對系統(tǒng)控件
#import "JHAnalyseControlAnalyseNode.h"
@implementation JHAnalyseControlAnalyseNode
+(void)load{
Method JH_sendAction = class_getInstanceMethod([UIControl class], @selector(sendAction:to:forEvent:));
class_addMethod([UIControl class], @selector(JHhook_sendAction:to:forEvent:), method_getImplementation(JH_sendAction), method_getTypeEncoding(JH_sendAction));
method_setImplementation(JH_sendAction, class_getMethodImplementation([self class], @selector(JHhook_sendAction:to:forEvent:)));
}
/**
替換的方法
*/
-(void)JHhook_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event{
NSString *methodName = NSStringFromSelector(action);
NSString *className = [NSString stringWithUTF8String: object_getClassName(target)];
UIControl *sender = (UIControl *)self;
//第一層,視圖ClassName
NSDictionary *data = [[JHAnalyseControlAnalyseNode class]getJsonData];
if ([[data allKeys]containsObject:className]) {
//第二層乳愉,Action
NSDictionary *class = data[className];
if([[class allKeys]containsObject:methodName]){
NSDictionary *action = class[methodName];
NSString *tag = [NSString stringWithFormat:@"%ld",sender.tag];
if([[action allKeys]containsObject:tag]){
NSDictionary *oneAction = action[tag];
NSLog(@"mtthodName=%@,className=%@,classRealName=%@tag=%@",methodName,className,oneAction[@"name"],tag);
//使用當(dāng)前時間表示最后操作時間
NSDate *date = [NSDate date];
NSTimeZone *zone = [NSTimeZone systemTimeZone];
NSInteger interval = [zone secondsFromGMTForDate: date];
NSDate *localeDate = [date dateByAddingTimeInterval: interval];
//組合數(shù)據(jù)并存入數(shù)據(jù)庫
NSDictionary *eventDic = @{@"eventClass":className,
@"eventCodeName":methodName,
@"eventCount":@"1",
@"eventDate":[NSString stringWithFormat:@"%@",localeDate],
@"eventName":oneAction[@"name"],
@"eventTag":tag,
@"eventUser":@"jianghong",
};
[JH_AnalyseDataHelper _AnalyseWithData:eventDic withType:AnalyseTypeEvent];
}
}
}
[self JHhook_sendAction:action to:target forEvent:event];
}
+(NSDictionary *)getJsonData{
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"analyse" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
return dic[@"event"];
}
配置的json文件(這個json文件最終將會配置到服務(wù)端作為下載數(shù)據(jù))
{
"viewController":{
"JHChatBaseController":"聊天主頁面",
"JHMapLocationVC":"定位",
"JHChildMessageVC":"消息",
"JHChildFriendsVC":"好友",
"JHNoteVC":"廣場",
"JHSquareVC":"筆記"
},
"event":{
"JHInputView":{
"_additionButtonAction:":{
"0":{
"class":"JHInputView",
"event":"_additionButtonAction:",
"tag":"0",
"name":"錄音"
},
"1":{
"class":"JHInputView",
"event":"_additionButtonAction:",
"tag":"1",
"name":"相冊"
},
"2":{
"class":"JHInputView",
"event":"_additionButtonAction:",
"tag":"2",
"name":"相機(jī)"
},
"3":{
"class":"JHInputView",
"event":"_additionButtonAction:",
"tag":"3",
"name":"定位"
}
},
"_sendAction":{
"0":{
"class":"JHInputView",
"event":"_sendAction",
"tag":"0",
"name":"發(fā)送信息"
}
}
},
"JHChatBaseCellVoice":{
"onPlayButton:":{
"0":{
"class":"JHChatBaseCellVoice",
"event":"onPlayButton:",
"tag":"0",
"name":"播放錄音"
}
}
}
}
}
CoreData數(shù)據(jù)庫操作
1.建立CoreDataModel對象
image.png
2.根據(jù)統(tǒng)計類型查詢數(shù)據(jù)庫更新數(shù)據(jù)庫
#import "JH_AnalyseDataHelper.h"
#define kManagedObjectContext [JH_ChatMessageManager sharedInstance].managedObjectContext
#define JH_EventAnalyseData @"EventAnalyseData"
#define JH_ViewControllerAnalyseData @"ViewControllerAnalyseData"
@implementation JH_AnalyseDataHelper
+(void)_AnalyseWithData:(NSDictionary *)data withType:(AnalyseType )analyseType{
if (analyseType==AnalyseTypeViewController) {
[self analyseVCWithData:data];
}else if (analyseType == AnalyseTypeEvent){
[self analyseEventWithData:data];
}
}
/**
分析統(tǒng)計頁面
*/
+(void)analyseVCWithData:(NSDictionary *)data{
//判斷是新建還是更新
NSArray *list = [self _searchViewControllerData];
//創(chuàng)建對應(yīng)的類
NSString *vcName = data[@"viewControllerName"];
for (ViewControllerAnalyseData *vcModel in list) {
if ([vcModel.viewControllerName isEqualToString:vcName]) {
//更新時長數(shù)據(jù)
vcModel.viewControllerTime = vcModel.viewControllerTime + [data[@"viewControllerTime"] floatValue];
[[JH_ChatMessageManager sharedInstance] saveContext]; //保存
return;
}
}
//創(chuàng)建一個新的
ViewControllerAnalyseData *vcModel = [NSEntityDescription insertNewObjectForEntityForName:JH_ViewControllerAnalyseData inManagedObjectContext:kManagedObjectContext];
for (NSString *str in [data allKeys]) {
if ([str isEqualToString:@"viewControllerTime"]) {
float time = [data[str] floatValue];
[vcModel setValue:@(time) forKey:str];
continue;
}
[vcModel setValue:data[str] forKey:str];
}
[[JH_ChatMessageManager sharedInstance] saveContext]; //保存
}
/**
分析統(tǒng)計事件
*/
+(void)analyseEventWithData:(NSDictionary *)data{
//判斷是新建還是更新
NSArray *list = [self _searchEventData];
//創(chuàng)建對應(yīng)的類
NSString *eventName = data[@"eventName"];
for (EventAnalyseData *vcModel in list) {
if ([vcModel.eventName isEqualToString:eventName]) {
//更新點擊次數(shù)數(shù)據(jù)
vcModel.eventCount = vcModel.eventCount + [data[@"eventCount"] integerValue];
[[JH_ChatMessageManager sharedInstance] saveContext]; //保存
return;
}
}
//創(chuàng)建一個新的
EventAnalyseData *eventModel = [NSEntityDescription insertNewObjectForEntityForName:JH_EventAnalyseData inManagedObjectContext:kManagedObjectContext];
for (NSString *str in [data allKeys]) {
if ([str isEqualToString:@"eventCount"]) {
NSInteger count = [data[str] integerValue];
[eventModel setValue:@(count) forKey:str];
continue;
}
[eventModel setValue:data[str] forKey:str];
}
[[JH_ChatMessageManager sharedInstance] saveContext]; //保存
}
#pragma mark - 查詢數(shù)據(jù)(暫時使用全部搜索)
+(NSArray *)_searchViewControllerData{
/**
數(shù)據(jù)查詢數(shù)據(jù)(全部)
*/
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:JH_ViewControllerAnalyseData
inManagedObjectContext:kManagedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSArray *objectResults = [kManagedObjectContext
executeFetchRequest:request
error:&error];
return objectResults;
}
+(NSArray *)_searchEventData{
/**
數(shù)據(jù)查詢數(shù)據(jù)(全部)
*/
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:JH_EventAnalyseData
inManagedObjectContext:kManagedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSArray *objectResults = [kManagedObjectContext
executeFetchRequest:request
error:&error];
return objectResults;
}