iOS无埋点数据统计实验

项目中需要统计的数据包括

1.在某个页面的停留时间(针对UIViewController)
2.某个事件(method)触发的次数
3.某个View的展示次数
(目前属于试验阶段)

1.停留时间runtime swap viewDidAppear & viewDidDisAppear

#import "UIViewController+JHswizzling.h"
static NSDate *startDate;
@implementation UIViewController (JHswizzling)
+ (void)load{
    
    
    // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidAppear:));
    Method toMethod = class_getInstanceMethod([self class], @selector(JH_swizzlingViewDidAppear));
    /**
     *  我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。
     *  而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。
     *  所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。
     */
    if (!class_addMethod([self class], @selector(JH_swizzlingViewDidAppear), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
    
    // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
    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);
    }
}

// 我们自己实现的方法,也就是和self的viewDidAppear方法进行交换的方法。
- (void)JH_swizzlingViewDidAppear{
    
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉
    NSDictionary *data = [self getJsonData];
    if ([[data allKeys]containsObject:str]) {
        
        if(![str containsString:@"UI"]){
            startDate = [NSDate  date];
            NSLog(@"统计打点出现 : %@ time : %@", [self getJsonData][str] ,startDate);
        }
    }
    
    [self JH_swizzlingViewDidAppear];
}

// 我们自己实现的方法,也就是和self的viewDidDisAppear方法进行交换的方法。
- (void)JH_swizzlingViewDidDisAppear{
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉
    NSDictionary *data = [self getJsonData];
    if ([[data allKeys]containsObject:str]) {
        if(![str containsString:@"UI"]){
            //计算时间差
            NSDate *endDate = [NSDate date];
            NSTimeInterval duration = [endDate timeIntervalSinceDate:startDate];
            NSLog(@"统计打点出现 : %@ time : %f 时长", data[str] ,duration);
            //组合数据并存入数据库
            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)触发的次数,针对系统控件

#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);
                //使用当前时间表示最后操作时间
                NSDate *date = [NSDate date];
                
                NSTimeZone *zone = [NSTimeZone systemTimeZone];
                
                NSInteger interval = [zone secondsFromGMTForDate: date];
                
                NSDate *localeDate = [date  dateByAddingTimeInterval: interval];
                
                //组合数据并存入数据库
                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文件最终将会配置到服务端作为下载数据)

{
    "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":"相机"
                },
                "3":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"3",
                    "name":"定位"
                }
            },
            "_sendAction":{
                "0":{
                    "class":"JHInputView",
                    "event":"_sendAction",
                    "tag":"0",
                    "name":"发送信息"
                }
            }
        },
        "JHChatBaseCellVoice":{
            "onPlayButton:":{
                "0":{
                    "class":"JHChatBaseCellVoice",
                    "event":"onPlayButton:",
                    "tag":"0",
                    "name":"播放录音"
                }
            }
        }
    }
}

CoreData数据库操作

1.建立CoreDataModel对象



image.png

2.根据统计类型查询数据库更新数据库

#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];
    }
    
    
}
/**
 分析统计页面
 */
+(void)analyseVCWithData:(NSDictionary *)data{
    //判断是新建还是更新
    NSArray *list = [self _searchViewControllerData];
    //创建对应的类
    NSString *vcName = data[@"viewControllerName"];
    
    for (ViewControllerAnalyseData *vcModel in list) {
        if ([vcModel.viewControllerName isEqualToString:vcName]) {
            //更新时长数据
            vcModel.viewControllerTime = vcModel.viewControllerTime + [data[@"viewControllerTime"] floatValue];
            [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
            return;
        }
    }
    //创建一个新的
    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]; //保存
}
/**
 分析统计事件
 */
+(void)analyseEventWithData:(NSDictionary *)data{
    //判断是新建还是更新
    NSArray *list = [self _searchEventData];
    //创建对应的类
    NSString *eventName = data[@"eventName"];
    
    for (EventAnalyseData *vcModel in list) {
        if ([vcModel.eventName isEqualToString:eventName]) {
            //更新点击次数数据
            vcModel.eventCount = vcModel.eventCount + [data[@"eventCount"] integerValue];
            [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
            return;
        }
    }
    
    //创建一个新的
    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 - 查询数据(暂时使用全部搜索)
+(NSArray *)_searchViewControllerData{
    /**
     数据查询数据(全部)
     */
    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{
    /**
     数据查询数据(全部)
     */
    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;
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 本篇文章是基于 网易乐得无埋点数据SDK 总结而成。负责无埋点数据收集 SDK 的开发已经有半年多了,期间在组内进...
    zerygao阅读 48,444评论 121 388
  • 1.背景说明 最近公司需要对用户行为进行深层次的数据分析和挖掘,需要用户详细的操作数据,想走无埋点的数据上报方式。...
    木鱼钟阅读 780评论 1 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,464评论 25 707
  • Jack张了了阅读 743评论 0 1