iOS - 无痕埋点+数据统计+记录用户行为轨迹

761 阅读2分钟
原文链接: www.jianshu.com
序言

该功能的实现是基于 Aspects 框架实现的,之前已经有对该框架做了深入的讲解,感兴趣的朋友可以先行阅读。iOS-Aspects框架详解

实现

调用Aspects框架的aspect_hookSelector方法对方法进行 hook

[BaseViewController aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo) {
    BaseViewController *vc = aspectInfo.instance;
    if (!vc.dataModel) vc.dataModel = [DataCubeModel new];
    // 未设置别名使用控制器名
    if (vc.dataModel.alias_page == nil) {
        NSDictionary *dict = [DataCubeTracker sharedIntance].cubeMapp;
        [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
            __block BOOL isContain = NO;
            if ([obj containsString:@","]) {
                NSArray *objArr = [obj componentsSeparatedByString:@","];
                [objArr enumerateObjectsUsingBlock:^(NSString *obj1, NSUInteger idx, BOOL *stop1) {
                    if ([obj1 isEqualToString:NSStringFromClass([vc class])]) {
                        isContain = YES;
                        *stop1 = YES;
                    }
                }];
            }else{
                if ([obj isEqualToString:NSStringFromClass([vc class])]) {
                    isContain = YES;
                }
            }
            if (isContain) {
                if ([key containsString:@"|"]) {
                    key = [[key componentsSeparatedByString:@"|"] firstObject];
                }
                vc.dataModel.alias_page = key;
                *stop = YES;
            }
        }];
    }
    if (vc.dataModel.alias_page == nil) {
        vc.dataModel.alias_page = NSStringFromClass([vc class]);
    }
} error:NULL];

简单解读一下上述方法

    1. 先定义一个 VC 的基类为BaseViewController,封装一个模型对象DataCubeModel,里面定义一些变量名,比如上一页 VC 名称,当前页 VC 名称等。基类定义该变量,如果当前控制器的别名为空,则根据之前定义好的给该控制器取一个名称。

事先定义一个字典,里面定义了当前工程所有控制器所对应的别名。

"NewPasswordPage|重置密码": "ChangePasswordViewController",
"CategoryIndexPage|分类页": "CategoryViewController",
二 对viewWillDisappear:viewDidDisappear:方法进行 hook,然后使用全局变量保存即将要离开页面的相关数据
  • UITabBarController类型 VC 处理
[BaseViewController aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo ,BOOL animated){
    BaseViewController *vc = aspectInfo.instance;
    UITabBarController *presentVC = (UITabBarController *)vc.presentingViewController;
    if(presentVC){//只有modal出来的才会走
        [CSLibs sharedInstance].tmpPageAlias = vc.dataModel.alias_page;
        [CSLibs sharedInstance].tmpVisitPage = vc.dataModel.visit_page;
    }
} error:NULL];
  • BaseViewControllerVC 处理
[BaseViewController aspect_hookSelector:@selector(viewDidDisappear:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo ,BOOL animated){
    BaseViewController *vc = aspectInfo.instance;

    //只走一次
    if (![CSLibs sharedInstance].tmpPageAlias) {
        [CSLibs sharedInstance].tmpPageAlias = vc.dataModel.alias_page;
    }
    if (![CSLibs sharedInstance].tmpVisitPage) {
        [CSLibs sharedInstance].tmpVisitPage = vc.dataModel.visit_page;
        if (![CSLibs sharedInstance].tmpVisitPage) {
            [CSLibs sharedInstance].tmpVisitPage = vc.dataModel.alias_page;
        }
    }
    
} error:NULL];
三 对viewDidAppear:方法进行 hook,然后对相关数据进行赋值操作
[BaseViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> aspectInfo ,BOOL animated){
    BaseViewController *vc = aspectInfo.instance;
    UITabBarController *presentVC = (UITabBarController *)vc.presentingViewController;
    NSString *aliasPrevPage = [[CSLibs sharedInstance].tmpPageAlias copy];
    NSString *prevPage = [[CSLibs sharedInstance].tmpVisitPage copy];
    if(presentVC){//只有modal出来的才会走
        UINavigationController *navi;
        if ([presentVC isKindOfClass:[UITabBarController class]]) {
            navi = [presentVC selectedViewController];
        } else if([presentVC isKindOfClass:[UINavigationController class]]){
            navi = (UINavigationController *)presentVC;
        }
        NSArray *viewcontrollers = navi.viewControllers;
        BaseViewController *lastVC = nil;
        if(viewcontrollers.count >= 1){
            lastVC = [viewcontrollers objectAtIndex:viewcontrollers.count - 1];
        }
        if(lastVC && [lastVC isKindOfClass:[BaseViewController class]]){
            aliasPrevPage = lastVC.dataModel.alias_page;
            prevPage = lastVC.dataModel.visit_page;
        }
    }
    vc.dataModel.alias_prev_page = aliasPrevPage;
    vc.dataModel.prev_page = prevPage;
    
    [CSLibs sharedInstance].tmpPageAlias = nil;
    [CSLibs sharedInstance].tmpVisitPage = nil;

} error:NULL];

这样就将用户浏览过的页面全部都记录了下来,然后根据需要进行传值或上报相关数据即可。