iOS無(wú)埋點(diǎn)數(shù)據(jù)統(tǒng)計(jì)實(shí)驗(yàn)

項(xiàng)目中需要統(tǒng)計(jì)的數(shù)據(jù)包括

1.在某個(gè)頁(yè)面的停留時(shí)間(針對(duì)UIViewController)
2.某個(gè)事件(method)觸發(fā)的次數(shù)
3.某個(gè)View的展示次數(shù)
(目前屬于試驗(yàn)階段)

1.停留時(shí)間runtime swap viewDidAppear & viewDidDisAppear

#import "UIViewController+JHswizzling.h"
static NSDate *startDate;
@implementation UIViewController (JHswizzling)
+ (void)load{
    
    
    // 通過(guò)class_getInstanceMethod()函數(shù)從當(dāng)前對(duì)象中的method list獲取method結(jié)構(gòu)體,如果是類(lèi)方法就使用class_getClassMethod()函數(shù)獲取。
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidAppear:));
    Method toMethod = class_getInstanceMethod([self class], @selector(JH_swizzlingViewDidAppear));
    /**
     *  我們?cè)谶@里使用class_addMethod()函數(shù)對(duì)Method Swizzling做了一層驗(yàn)證,如果self沒(méi)有實(shí)現(xiàn)被交換的方法,會(huì)導(dǎo)致失敗。
     *  而且self沒(méi)有交換的方法實(shí)現(xiàn),但是父類(lèi)有這個(gè)方法,這樣就會(huì)調(diào)用父類(lèi)的方法,結(jié)果就不是我們想要的結(jié)果了。
     *  所以我們?cè)谶@里通過(guò)class_addMethod()的驗(yàn)證,如果self實(shí)現(xiàn)了這個(gè)方法,class_addMethod()函數(shù)將會(huì)返回NO,我們就可以對(duì)其進(jìn)行交換了。
     */
    if (!class_addMethod([self class], @selector(JH_swizzlingViewDidAppear), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
    
    // 通過(guò)class_getInstanceMethod()函數(shù)從當(dāng)前對(duì)象中的method list獲取method結(jié)構(gòu)體,如果是類(lèi)方法就使用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);
    }
}

// 我們自己實(shí)現(xiàn)的方法,也就是和self的viewDidAppear方法進(jìn)行交換的方法。
- (void)JH_swizzlingViewDidAppear{
    
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我們?cè)谶@里加一個(gè)判斷,將系統(tǒng)的UIViewController的對(duì)象剔除掉
    NSDictionary *data = [self getJsonData];
    if ([[data allKeys]containsObject:str]) {
        
        if(![str containsString:@"UI"]){
            startDate = [NSDate  date];
            NSLog(@"統(tǒng)計(jì)打點(diǎn)出現(xiàn) : %@ time : %@", [self getJsonData][str] ,startDate);
        }
    }
    
    [self JH_swizzlingViewDidAppear];
}

// 我們自己實(shí)現(xiàn)的方法,也就是和self的viewDidDisAppear方法進(jìn)行交換的方法。
- (void)JH_swizzlingViewDidDisAppear{
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我們?cè)谶@里加一個(gè)判斷,將系統(tǒng)的UIViewController的對(duì)象剔除掉
    NSDictionary *data = [self getJsonData];
    if ([[data allKeys]containsObject:str]) {
        if(![str containsString:@"UI"]){
            //計(jì)算時(shí)間差
            NSDate *endDate = [NSDate date];
            NSTimeInterval duration = [endDate timeIntervalSinceDate:startDate];
            NSLog(@"統(tǒng)計(jì)打點(diǎn)出現(xiàn) : %@ time : %f 時(shí)長(zhǎng)", data[str] ,duration);
            //組合數(shù)據(jù)并存入數(shù)據(jù)庫(kù)
            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.某個(gè)事件(method)觸發(fā)的次數(shù),針對(duì)系統(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)前時(shí)間表示最后操作時(shí)間
                NSDate *date = [NSDate date];
                
                NSTimeZone *zone = [NSTimeZone systemTimeZone];
                
                NSInteger interval = [zone secondsFromGMTForDate: date];
                
                NSDate *localeDate = [date  dateByAddingTimeInterval: interval];
                
                //組合數(shù)據(jù)并存入數(shù)據(jù)庫(kù)
                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文件(這個(gè)json文件最終將會(huì)配置到服務(wù)端作為下載數(shù)據(jù))

{
    "viewController":{
        "JHChatBaseController":"聊天主頁(yè)面",
        "JHMapLocationVC":"定位",
        "JHChildMessageVC":"消息",
        "JHChildFriendsVC":"好友",
        "JHNoteVC":"廣場(chǎng)",
        "JHSquareVC":"筆記"
    },
    "event":{
        "JHInputView":{
            "_additionButtonAction:":{
                "0":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"0",
                    "name":"錄音"
                },
                "1":{
                    "class":"JHInputView",
                    "event":"_additionButtonAction:",
                    "tag":"1",
                    "name":"相冊(cè)"
                },
                "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ù)庫(kù)操作

1.建立CoreDataModel對(duì)象



image.png

2.根據(jù)統(tǒng)計(jì)類(lèi)型查詢(xún)數(shù)據(jù)庫(kù)更新數(shù)據(jù)庫(kù)

#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)計(jì)頁(yè)面
 */
+(void)analyseVCWithData:(NSDictionary *)data{
    //判斷是新建還是更新
    NSArray *list = [self _searchViewControllerData];
    //創(chuàng)建對(duì)應(yīng)的類(lèi)
    NSString *vcName = data[@"viewControllerName"];
    
    for (ViewControllerAnalyseData *vcModel in list) {
        if ([vcModel.viewControllerName isEqualToString:vcName]) {
            //更新時(shí)長(zhǎng)數(shù)據(jù)
            vcModel.viewControllerTime = vcModel.viewControllerTime + [data[@"viewControllerTime"] floatValue];
            [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
            return;
        }
    }
    //創(chuàng)建一個(gè)新的
    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)計(jì)事件
 */
+(void)analyseEventWithData:(NSDictionary *)data{
    //判斷是新建還是更新
    NSArray *list = [self _searchEventData];
    //創(chuàng)建對(duì)應(yīng)的類(lèi)
    NSString *eventName = data[@"eventName"];
    
    for (EventAnalyseData *vcModel in list) {
        if ([vcModel.eventName isEqualToString:eventName]) {
            //更新點(diǎn)擊次數(shù)數(shù)據(jù)
            vcModel.eventCount = vcModel.eventCount + [data[@"eventCount"] integerValue];
            [[JH_ChatMessageManager sharedInstance] saveContext]; //保存
            return;
        }
    }
    
    //創(chuàng)建一個(gè)新的
    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 - 查詢(xún)數(shù)據(jù)(暫時(shí)使用全部搜索)
+(NSArray *)_searchViewControllerData{
    /**
     數(shù)據(jù)查詢(xún)數(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ù)查詢(xún)數(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;
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容