iOS无埋点数据统计实验

2017-07-06  本文已影响0人  Jneth

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

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;
    
}
上一篇下一篇

猜你喜欢

热点阅读