iOS自动化埋点的实现

Published 2018-10-17 13:51 1083 words 6 min read

前端项目通过bridge获取客户端资源,客户端直接返回response对象Win11 光标变白消失解决方案(浏览器开启硬件加速后,在鼠标滑到文本处出现)Terminal里用option/alt + 鼠标点击,就可以让光标到指定位置Duff's Device为 UIControl 实现线程安全的 Block 事件扩展:原理与实践前端代码规范设计模式k8s前端项目利用nginx部署项目不停机Vue3+Vite+history模式+Nginx 部署二级路径iOS开发工具常用代码模板2——数据结构常用代码模板1——基础算法iOS:特殊符号大全HomeBrew 更新报错“fatal- Could not resolve HEAD to a revision”SwiftUI基本控件代码大全——1.欢迎进入软件构建的世界C++ STL简介LeetCode 70 爬楼梯(青蛙跳台阶)深入理解计算机系统(第二版) 家庭作业 第二章Swift Module 如何被全局引用LeetCode 398 随机数索引Vue 的一些指令和缩写LeetCode 486 Predict the Winner(预测赢家)浅谈iOS中的weakCocoaPods组件化——OC/Swift动静态库混用OpenGL 术语当对象接收到不能处理的消息时调用的方法命令行通过ffmpeg剪切视频iOS:如何在UITableView调用reloadData刷新结束后再同步执行后续操作统计iOS工程代码行数CMake 入门及常用变量指令手册LeetCode 6 ZigZag Conversion(Z字转换)LeetCode 106 Construct Binary Tree from Inorder and Postorder Traversal(由中序和后序遍历建立二叉树)LeetCode 5 Longest Palindromic Substring(最长回文字串)LeetCode 8 String to Integer (atoi)Objective-C Type EncodingsObjective-C:为什么分类中不能直接添加属性MeshLab数据结构与算法解析习题2.23数据结构与算法解析习题2.19数据结构与算法解析习题2.16数据结构与算法解析习题2.14数据结构与算法解析习题2.13数据结构与算法解析习题2.12数据结构与算法解析习题2.11:二分查找数据结构与算法解析习题2.10:霍纳法则(Horner's rule)数据结构与算法解析习题2.7数据结构与算法解析习题1.3数据结构与算法解析习题1.2数据结构与算法解析习题1.1centos7 mysql数据库安装和配置Fishhook-源码分析解决Mac下 Cornerstone 无法查看SVN日志的问题UIButton扩大点击范围以及关于响应者链条的思考UILabel中文带行间距的处理,限制行数,计算高度等UITableview调用reload方法时抖动问题C语言指针小结C语言数据类型和const、static关键字iOS 截取整个 scrollview 图片WxWidgets类列表objc源码分析-runtime-classiOS自动化埋点的实现iOS平台编译Ogre游戏引擎库在 Mac OS X 上安装 TensorFlowiPhone 刘海机型UI适配(X、Xs、Xs Max、Xr)关于C语言中的union虚拟机 Linux 上编 slice3r perl Build.PL 报错iOS 沙盒与 BundleCOCOAPODS技巧-创建私有仓库iOS脚本打包 ipa(.app转.ipa)Objective-C 中禁止调用指定的方法iOS 关于 UITextField 的字数限制升级 Mac OS 10.13 High Sierra 后 ruby 报错iOS 框架学习-AsyncSocketSQL语句OC优缺点以及常见bugUIViewController 的生命周期runtime——运行时简单使用iPhone6 Plus上面神秘的缝隙CocoaPods安装和使用教程(更新至Mac High Sierra)UIApplicationiOS 网络小结NSString的各种处理HTTP请求报文和HTTP响应报文,以及工作原理Vim 快捷键整理OC中nil 、NULL、 Nil 、NSNull的区别GCD 使用指南iOS常用数据类型转换关于NSNotificationCenterGit 命令MAC OS 常用技巧Description方法和NSLog函数代码重构OC单例宏Block in Objective-CObjective-C 语法 3Objective-C 语法 2Objective-C 语法 1
This post is not yet available in English. Showing the original.
一、埋点的作用: 应用趋势分析:清晰展现应用的新增用户、活跃用户、启动次数、版本分布、行业指标等数据,方便从整体掌控应用的运营情况及增长动态。 渠道分析:在哪里推广最有效?从哪里获取的用户最有价值?通过数据对比评估不同渠道的用户质量和活跃程度,从而衡量推广效果。 留存分析:可以掌握每日(周/月)的新

一、埋点的作用:

  1. 应用趋势分析:清晰展现应用的新增用户、活跃用户、启动次数、版本分布、行业指标等数据,方便从整体掌控应用的运营情况及增长动态。
  2. 渠道分析:在哪里推广最有效?从哪里获取的用户最有价值?通过数据对比评估不同渠道的用户质量和活跃程度,从而衡量推广效果。
  3. 留存分析:可以掌握每日(周/月)的新增用户在初次使用后一段时间内的留存率,留存率的高低一定程度上反映了产品和用户质量的好坏。
  4. 用户属性:用户的基本属性和行为特征,全面了解用户。
  5. 行为分析:针对性地进行应用内的数据统计,了解用户的产品使用细节及行为特征,帮助团队寻找产品改进的突破点,评估产品优化的效果。

二、自动化埋点SDK的研发背景

1、代码埋点

优点:灵活性高,能满足大量个性化需求。

缺点:开发者需要手动在需要埋点的节点植入埋点代码,可能埋点代码也需要植入一定的业务逻辑。
​ 代码耦合严重,复用性差,工作量大,难以维护。

2、自动化埋点

优点:

  1. 可以较大程度降低开发成本,不受版本更新影响 。
  2. 解耦业务代码,易维护,可移植性强。
  3. 解决了数据回溯问题,可查看历史数据。
  4. 避免了使用三方SDK可能造成用户关键数据丢失及企业泄密等问题。

缺点:未解决个性化自定义获取数据的问题,缺乏数据获取的灵活性。

三、数据采集

数据采集原理:利用object-c的runtime机制,对有需要的类和事件进行方法交换,进行事件拦截,注入埋点代码,实现数据统计的功能,具体做法是:

重载类的+(void)load方法,在程序加载到内存时利用runtime的method_exchangeImplementations等接口, 将方法(设为M)的实现互相交换,当方法M被调用时就会被Hook,执行我们的方法。

/// ClickKit 类
+ (void)swapMethod:(Class)class origMethod:(SEL)origSelector newMethod:(SEL)newSelector{
    Method originalMethod = class_getInstanceMethod(class, origSelector);
    Method swizzledMethod = class_getInstanceMethod(class, newSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

+ (BOOL)addMethod:(Class)class sel:(SEL)sel method:(IMP)method
{
    return class_addMethod(class, sel, method, "v@:@@");
}

(1)页面统计(PV)

PV统计原理:通过hook UIViewController的以下函数,达到采集类名等功能:

特别注意:必须实现以下三个函数的super调用,否则,页面自动化埋点无法触发

- (void)viewDidLoad;
- (void)viewDidAppear:(BOOL)animated;
- (void)viewDidDisappear:(BOOL)animated;

(2)事件统计(event)

事件id:通过对点击事件进行方法拦截,获取当前点击控件及控件所属target,根据点击视图的响应者链,逐级取到控件的所属view tree,建议点击视图增加tag。

针对单一点击事件,以UIControl为例:

@implementation UIControl (ClickKit)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [ClickKit swapMethod:[self class]
                  origMethod:@selector(sendAction:to:forEvent:)
                   newMethod:@selector(swizzle_sendAction:to:forEvent:)];
    });
}

- (void)swizzle_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    [self swizzle_sendAction:action to:target forEvent:event];
    
	/// 收集数据
}

@end

针对列表点击事件,以UITableView为例:

@implementation UIScrollView (ClickKit)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [ClickKit swapMethod:[self class]
                    origMethod:@selector(setDelegate:)
                     newMethod:@selector(swizzle_setDelegate:)];
    });
}


- (void)swizzle_setDelegate:(id<UIScrollViewDelegate>)delegate
{
    [self swizzle_setDelegate:delegate];

    if ([self isKindOfClass:[UITableView class]]) {
            [(UITableView *)self swizzle_tableViewDidSelectRowAtIndexPathInClass:delegate];
    }
    else if ([self isKindOfClass:[UICollectionView class]]) {
            [(UICollectionView *)self swizzle_collectionViewDidSelectRowAtIndexPathInClass:delegate];
    }
}

@end
    
@implementation UITableView (ClickKit)

- (void)swizzle_tableViewDidSelectRowAtIndexPathInClass:(id)delegate
{

    if ([delegate isKindOfClass:[UITableView class]]) {
        return;
    }
    if ([ClickKit hasMethod:[delegate class] sel:@selector(tableView:didSelectRowAtIndexPath:)])
    {
        SEL swizSel = NSSelectorFromString(@"swizzle_didSelectRowAtIndexPath");
        if ([ClickKit addMethod:[delegate class] sel:swizSel method:(IMP)swizzle_didSelectRowAtIndexPath]) {
            [ClickKit swapMethod:[delegate class] origMethod:swizSel newMethod:@selector(tableView:didSelectRowAtIndexPath:)];
        }
    }
}

void swizzle_didSelectRowAtIndexPath(id self, SEL _cmd, id tableView, id indexPath)
{
    SEL selector = NSSelectorFromString(@"swizzle_didSelectRowAtIndexPath");
    ((void(*)(id, SEL, id, id))objc_msgSend)(self, selector, tableView, indexPath);

    /// 收集数据

}

@end

四、数据存储

埋点数据采用db方式进行数据存储,一般依据事件类型,db结构由启动表、pv表和event表组成。具体怎么组织数据结构,需要上报什么数据,需要和大数据沟通。

五、数据上报

上报策略: 1、采用实时上传和离线上传相结合的方式,wifi和4G模式下,pv采用实时上报的方式,事件是随着下一个pv同时上传。 2、离线上传:其它网络情况下只做存储处理。

这个策略也只是建议,具体还看业务需求。

17001909398580.png
17001909398580.png