aop埋点 ios
2018-08-30 15:18:53 qq_33608748 阅读数 452

埋点的目的主要是为了分析统计App使用的情况

一般埋点主要用于界面,action事件

界面中的数据一般都是静态的 和业务逻辑不相关, 一般是进入离开时间,是否进入该界面

action事件交互统计 ,一般是点击  双击 手势等交互

方案

1 使用第三方sdk: 例如:umeng统计 百度 腾讯MTA 等

2  手动埋点:常规的做法是将埋点代码封装成工具类,凡是工程中需要埋点(如点击事件、页面跳转)的地方都应用埋点代码。

  a)主要统计的数据为:当前页面,页面停留时间(进入与离开时间差计算),点击事件等。缺点是代码散乱不易维护

  b)记录每个页面进入退出(viewWillAppear与viewWillDisappear)的时间,用来计算页面停留时间,把触发时间以及一些点击行为操作全记到一个模型对象中,然后在页面离开或点击事件触发把这个模型写入到数据表中,到时候只要从表中取出数据再发送给后台即可实现埋点。

 c)为防止对后台接口造成数据攻击,每5分钟从数据库中取一次数据发送,每200条列表数据向服务发送,直到本次取出所有数据全部发送完毕.

3.无码埋点

Method Swizzling、Hook与代码注入,基于运行时的埋点方案

iOS埋点
2017-06-01 16:29:05 chenxi506343891 阅读数 2654

1.摘要
 如果你的公司用户基数足够大,那么根据埋点数据分析得到的反馈是非常有用的。除了第三方的埋点数据分析,比如友盟,如果我们希望自己也存一份数据,作为对友盟数据的对比,在iOS工程中如何实现呢?
   一般的埋点分为PV(界面级别的),PA(action事件级别).下边为了减少冗余,简称PV,PA。PV中的数据一般都是静态的,与业务逻辑不相关,比如进入时间,离开时间,是否进入该界面等等。
   所以这类需求是很好处理的,一般都会让所有的controller都继承BaseViewController。这样左右的信息都基于一个类中,符合高聚合的表现。
   那么PA的事件埋点数据如何处理呢?最传统的方法是在每个action中添加相应的埋点数据,但是这样功能和业务逻辑就混在一起,代码结构很混乱,不简洁,不优雅。
   混在在一起,放眼望去,怎么埋点到处都是啊,自己都烦的不行。尤其是每一次action改变之后,对应的逻辑就要改变,我要去不同的类中去更改。实在是不胜其烦。
   其实早就有解决之道,而且非常好用,这里推荐MOAspect。它校于Aspect,有一个功能很重要就是支持相同的方法名注册,而Aspect是不支持不同类相同方法名注册的。
   这些都是都是在去年经过验证,并且线上反馈还不错的。大家可以放心去用,坑都是踩过的。

做产品的同学在产品上线后经常离不开一个词,数据分析。那么要如何进行数据分析呢?不妨先问自己这么几个问题。

  • 你要分析什么问题?是找问题还是验证?
  • 关于这些问题你需要哪些数据?
  • 这些数据从哪里来?

要怎么解决这些问题呢?答案是数据埋点。首先通过产品定位及目标来确定自己需要哪些数据,其次通过在产品各个流程环节中设置数据埋点,最后,当用户使用产品时,后台就能源源不断地接收到数据了。

前几天看到一个段子,论数据分析的重要性:
一个哥们,有次聚会时说自己买了很多条iPhone数据线,家里的每个房间插一条,走到哪都能随时充电。在场的姑娘们都当成是段子笑笑都过去了,只有个细心的姑娘悄悄问他买了多少条,他说42条。现在她和他在北京三环内总共42个房间的数套豪宅愉快地生活,下个月结婚。

2. 客户端埋点方案

  • 使用第三方统计SDK,如:友盟统计等。
  • 自己手动埋点。
    本文讲述的是自己手动埋点。
    本文埋点主要统计的数据为:当前页面,页面停留时间(进入与退出时间差计算),点击事件等。
    原理:记录每个页面进入退出(viewWillAppear与viewWillDisappear)的时间,用来计算页面停留时间,把触发时间以及一些点击行为操作全记到一个模型对象中,然后在页面离开或点击事件触发把这个模型写入到数据表中,到时候只要从表中取出数据再发送给后台即可实现埋点。写的不是很清楚,详情见demo

3.优化处理

为防止对后台接口造成数据攻击,每5分钟从数据库中取一次数据发送,每200条列表数据向服务发送,直到本次取出所有数据全部发送完毕。

2019-04-29 09:49:00 weixin_34266504 阅读数 28

简单介绍一下 AOP

无痕埋点最重要的技术是将埋点代码从业务代码中剥离,放到独立的模块中的技术。写业务的同学只需按照正常的设计思路编写业务代码,写埋点的同学通过 AOP 技术往业务代码中插入埋点代码。

AOP 全称叫 Aspect-Oriented Programming,中文名叫面向切面变成。网络上有关于面向切面编程的名词解释。通俗一点,即可使用下图来说明。

如果希望某个函数的实现逻辑如下:

2716570-58103145667a10e1.png
希望逻辑

代码块 1代码块 2 之间的逻辑毫无干系,是两块独立的逻辑,那么同时写在一个文件的一个函数中会造成代码臃肿,不易维护。

如果能将两个代码分离,编写在不同文件中,即可简化逻辑,增加代码的可阅读性:

2716570-5427be3a81169917.png
编码时

上图为编码时的代码逻辑图,两份代码块写在两个不同的方法中。当使用 AOP 技术后,软件在运行时就会将两份逻辑合并到一个方法中,变成当初期望的逻辑的样子。

2716570-58103145667a10e1.png
运行时

在 iOS 平台中,AOP 一般使用基于 ObjC 动态性质以及消息转发的功能实现的。ObjC 的动态性就是所谓的 Runtime。利用 Runtime 可以对一个类的某个方法进行修改,从而实现运行时的逻辑变化。而消息转发则是对面向对象的方法调用的拦截,增加自己特定的处理。Runtime 配合消息转发就能实现 iOS 平台上基本的 AOP 需求。

目前,能够完成 iOS 的 AOP 的库有:ReactiveCocoa、RxSwift、Aspects 等等。RAC 和 Rx 系列则是为了响应式编程而出现的,他们功能强大,但也很重。如果仅仅只是为了无痕埋点,引入响应式变成则需要很大的成本,也是大材小用。Aspects 则是专门为 AOP 设计的,底层学习苹果 KVO 实现机制,可以以对象为颗粒进行 AOP。曾经我们的项目引入了这个库,但如果业务已经实现了 - (void)forwardInvocation:(NSInvocation *)invocation 方法,又被 Aspects 进行 AOP 后,业务的消息转发机制就会被覆盖,造成不可预计的后果*。

*关于 Aspects 的 bug:

在去年使用时,发现 Aspects 与 JSPatch(都使用了 - forwardInvocation: 方法)不兼容。主要表现是当 JSPatch 先生效,Aspects 后生效的情况下,JSPatch 的消息转发机制断裂,无法完成补丁修复,甚至 Crash。

而我们在业务上也用到了 - forwardInvocation: 方法,即使没有引入 JSPatch,也依旧会出现同样的问题。

在 Issue 里查找了一番,发现是 class_replaceMethod 的 bug (Merge),但至今都没有修复这个问题(已并入 master,但是在 tag 1.4.2 之后并入的,而 CocoaPods 上最新发布的版本是 1.4.1)。

所以在不引入臃肿的三方库外,如何既安全又准确地使用 AOP 进行无痕埋点,就是本文即将讨论的内容。

大部分的无痕埋点实现方案的弊端

在丢弃 Aspects 后,就寻找无痕埋点的解决方案。百度搜索 iOS 无痕埋点 关键字,得到的结果几乎一样。贴上搜索结果第一篇的地址:简书 - iOS无痕埋点方案分享探究 by: SandyLoo

在此先非常感谢作者的分享,此方案给我提供了几个版本的稳定的无痕埋点。

但是在某次埋点 SDK 重构的过程中,发现了此方案的多处弊端,由于网络上大部分的无痕埋点方案都与此大同小异,所以就用此例子来分析这系列的方案存在的隐患和缺陷。

方案基本介绍

无痕埋点主要是记录用户的操作,而用户的操作无非就是按钮的点击和列表的选择,所以无痕埋点的需求即是对用户点击的响应方法进行 AOP 处理,插入对应埋点方法。

UIKit 提供的用户交互接口里,主要可以分为两大类:

  1. Delegate 类(UITableViewUICollectionView 的点击事件,特点是方法名定死,使用 weak 属性持有响应对象)
  2. Target-Action 类(UIControlUIGestureRecognizer 的回调事件,特点是方法名可自定义,方法参数可有可无,使用 weak 属性持有响应对象,支持多个响应者)

此方案也对这几种接口提供了不同的 AOP 代码。

1. UITableView 与 UICollectionView

这两种对象归结到第一类中(下文主要讲解 UITableViewUICollectionView 同理就不解释),业务通过实现 - tableView:didSelectRowAtIndex: 方法来捕获用户点击事件。此方法的方法签名(由返回值类型和参数类型编码而成)因 UITableViewDelegate 的定义而被定死,所以可以很好的完成 AOP 代码。

  1. 使用 runtime 对 -[UITableView setDelegate:] 进行方法交换,插入 delegate 的捕获代码。
  2. 当捕获到 delegate 对象时(一般为 ViewController),获取该对象的类。
  3. 构建临时方法名:aop_tableView:didSelectRowAtIndex:,判断 2 中的类是否有这个方法。
  4. 如果有,说明此类被处理过,则不继续。
  5. 如果没有,将预先写好的 static 函数,通过 runtime 构建新的 Method 实例(方法名是 3 中的方法名),添加到类中。
  6. 将 5 添加的方法和原方法进行方法交换。

其中步骤 5 中预先写好的 static 函数大致如下:

static void AOP_tableView_didSelectRowAtIndex(id<UITableViewDelegate> self, SEL _cmd, UITableView *tableView, NSIndexPath *indexPath) {
    //  先调用业务代码
    SEL origSel = @selector(aop_tableView:didSelectRowAtIndex:);
    [self performSelector:origSel withObject:tableView withObject:indexPath];
    
    //  再埋点
    [[Tracker sharedTracker] trackEvent:@"xxxxx"];
}

分析一下此方案的弊端:

缺陷 1: 继承导致重复埋点

如果某个业务代码如下:

2716570-6c9a7dbe37d5270f.png
业务代码

如果出现了某种代码执行顺序:

  1. 子类实例化
  2. 父类实例化

则会出现如下情况:

  1. 捕获到子类的对象,发现没有经过 AOP,则进行 AOP 处理,产生如下代码:
2716570-e777de12f93729f6.png
子类对象被 AOP

捕获到父类,发现没有经过 AOP (步骤 1 是在子类处理,所以父类无法检测),则进行 AOP 处理,产生如下代码:

2716570-85b79dc8be264cd5.png
父类对象被 AOP

此时,如果 1 实例化出来的子类对象还存在,或者在这之后实例化了新的子类对象,对应的埋点代码逻辑会执行两次,逻辑如下:

2716570-c3a3256ea69967eb.png
最终子类会执行两次埋点逻辑

缺陷 2: 如果业务手动执行 tableView:didSelectRowAtIndex: 也会触发埋点

手动执行应当是代码产生的,而非用户真实点击。即使正常开发不会这么做,但是如果真的这么做了,就会产生一次不必要的埋点数据。

缺陷 3: 如果业务使用了 _cmd 参数,可能取到错误的 SEL

上述文章中做了处理,不会有这种问题。但网络上依旧有使用 performSelector 方法或通过声明方法然后使用中括号语法来调用原方法代码,这种方式会导致传递给业务的 _cmd 参数是 AOP 的 SEL,也就是上文的 aop_tableView:didSelectRowAtIndexPath:。如果业务方用到了这个 _cmd 参数,则会出现和预期不一样的数据。

2. UIGestureRecognizer

手势的回调接口是 target-action,通过添加 target(回调对象) 和 action(对应的回调方法) 对,来完成手势触发的回调。手势可以归结到上述分类中的第二类。

UITableView 相比,最大的差异是方法名需要动态获取,所以需要一个新的 AOP 逻辑:

  1. 使用 runtime 对 -[UIGestureRecognizer initWithTarget:action:] 进行方法交换,插入捕获 targetaction 的代码。
  2. 捕获到 action 时,添加特殊前缀,得到 aop_action,并判断 target 的类是否拥有 aop_action 方法。
  3. 如果有则说明此 target 对应的类已做 AOP 处理。
  4. 如果没有,则通过预先写好的 static 函数和 aop_action 创建一个 Method,添加到 target 的类中。
  5. 将 4 添加的 aop_action 方法和原 action 方法进行方法交换。

其中步骤 4 中的方法大致如下:

static void AOP_gestureRecognizerAction(id self, SEL _cmd, UIGestureRecognizer *sender) {
    //   调用原方法
    NSString *sel = [@"aop_" stringByAppendingString:NSStringFromSelector(_cmd)];
    [self performSelector:NSSelectorFromString(sel) withObject:sender];
    
    //  埋点
    [[Tracker sharedTracker] trackEvent:@"xxxxx"];
}

缺陷

手势的缺陷将与 UIControl 的缺陷一并介绍。

3. UIControl

UIControl 和手势类似,也是使用 target-action 接口来回调控件状态变化事件。但这里需要和手势分开介绍。

与手势不同的是,UIControl 并不是通过动态添加方法来完成无痕埋点,而是直接拦截系统方法(调用 action 方法的方法)来完成埋点。具体如下:

  1. 使用 runtime 对 -[UIControl sendAction:to:forEvent:] 进行方法交换,插入捕获发送事件的代码。
  2. 捕获到发送事件时,埋点。

UIGestureRecognizer 和 UIControl 的缺陷

缺陷1: 没有携带 sender 参数的 action 可能会导致手势埋点闪退

由于手势预先写好了函数,而函数的参数列表包含了手势本身(也就是通常的 sender 参数),但是业务写的方法不一定都包含 sender 参数,所以这里会有隐患。

缺陷2: 手势埋点会遗漏使用 addTarget:action: 方法添加的回调

上述文章中只 hook 了初始化方法,并没有 hook add 方法,所以如果业务使用 init 方法创建对象,再用 add 方法添加回调,埋点就会遗漏。

但这不是大问题。

缺陷3: 当业务同时绑定手势和控件到同一个方法时可能会导致闪退

如果手势和按钮绑定到同一个 action 时,并且手势和按钮都被当做 sender 参数传入到 action 中。当按钮点击时,就会触发手势埋点,如果手势埋点取了手势的 view 就会闪退:

static void AOP_gestureRecognizerAction(id self, SEL _cmd, UIGestureRecognizer *sender) {
    //   用户点击了按钮,但是还是走了手势的埋点,并且 sender 是个按钮对象

    //   调用原方法
    NSString *sel = [@"aop_" stringByAppendingString:NSStringFromSelector(_cmd)];
    [self performSelector:NSSelectorFromString(sel) withObject:sender];

    //  取 View 来构建埋点参数
    UIView *view = sender.view;   //  此处会闪退,因为传进来可能是 UIButton
    UIViewController *controller = sender.nextResponse;
    [[Tracker sharedTracker] trackEvent:@"xxxxx"];
}

手势和按钮绑定到同一个 action 的业务场景举例:ActionSheet

如果你自己实现了 ActionSheet,你可能会在 ActionSheet 上方阴影部分添加手势来隐藏 ActionSheet ,同时你也会增加一个取消按钮,来隐藏 ActionSheet。这两个绑定的 action 可能会是同一个方法。

缺陷4: 如果缺陷 2 不闪退,UIControl 触发事件会导致两次埋点

即使缺陷 3 没有导致闪退,UIControl 的埋点在 - sendAction:to:forEvent: 方法中,手势的埋点在 action 方法中。用户点击了按钮,先执行 - sendAction:to:forEvent: 的埋点,然后执行业务的 action,也就会执行手势的埋点。这就导致产生了一次垃圾数据

缺陷5: 如果业务手动调用 action 会导致不必要的手势埋点

如果业务主动调了一次 action,并非用户实际操作,手势埋点替换 action 的方式下也会被埋点。这样也产生了一次垃圾数据。

缺陷6: 移除 target-action 后埋点依旧有效

移除 target-action 时并不能恢复被 runtime 改造的 action 方法,所以埋点依旧生效。

无痕埋点方案实现

为了让业务代码安全无误的执行,我们必须尽可能地不修改业务代码,也就是不会对业务代码进行 Runtime 处理。

在遵循此原则下,我们设计了如下的无痕埋点解决方案。

Delegate 系列无痕埋点

UITableView 介绍 Delegate 的无痕埋点。

在没有无痕埋点的情况下,ControllerUITableView 的持有关系如下:

2716570-fa144cab811f271d.png
ViewController 和 Table View 持有关系

为了尽可能的不修改 View Controller 的内容,我们为 delegate 那条虚线添加了一个中间对象 Proxy:

2716570-6891f46bbf207c9d.png
插入一个中间对象

同时为了防止 Proxy 销毁,我们使用关联对象让 TableView 强引用 Proxy。

为何不是 Controller 强持有 Proxy?

因为一个 table view 只能有一个 delegate,但是一个 controller 可以成为多个 table view 的 delegate

为何 Proxy 是弱引用 View Controller 而不是强引用?

因为不管是 VC 持有 Proxy 还是 TableView 持有 Proxy,只要 Proxy 持有 VC,就会产生循环引用。

而大体的步骤如下:

  1. 使用 runtime 对 -[UITabelView setDelegate:] 进行方法交换,替换 setter。
  2. 创建 Proxy 对象。
  3. 将 delegate 传递给 Proxy 对象。
  4. 将 Proxy 当做新的 delegate 参数传递给原方法
  5. 将 TableView 使用关联对象强持有 Proxy

而 Proxy 要做的工作:

  1. 拦截 - tableView:didSelectRowAtIndexPath: 方法,并做埋点,同时将此事件转发给 View Controller
  2. 由于拦截了 delegate,就会拦截所有 delegate 方法,所以 Proxy 要模拟 View Controller 对 UITableViewDelegate 协议中的几个方法的响应情况。
  3. 对于 View Controller 实现的方法,需要将事件转发给 View Controller。

Proxy 的代码大致如下:

//  self.delegate 就是实际的 View Controller

//  拦截此 delegate 方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) {
        [self.delegate tableView:tableView didSelectRowAtIndexPath:indexPath];
    }
    
    //  埋点
}

//  使用以下四个方法来模拟 delegate 其他方法的响应以及转发这些方法

- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
    BOOL conforms = [super conformsToProtocol:aProtocol];
    if (!conforms) {
        conforms = [self.delegate conformsToProtocol:aProtocol];
    }
    return conforms;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    BOOL hasResponds = [super respondsToSelector:aSelector];
    hasResponds |= [self.delegate respondsToSelector:aSelector];
    return hasResponds;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    NSMethodSignature *methodSignature = [super methodSignatureForSelector:selector];
    if (!methodSignature) {
        if ([self.delegate respondsToSelector:selector]) {
            methodSignature = [(NSObject *)self.delegate methodSignatureForSelector:selector];
        }
    }
    return methodSignature;
}

- (void)forwardInvocation:(NSInvocation*)invocation {
    [invocation invokeWithTarget:self.delegate];
}

经过以上处理,ViewController 的代码无需任何改动,通过中间对象来拦截 - tableView:didSelectRowAtIndexPath: 方法来实现埋点。遵循尽可能不修改业务代码的原则。

Target-Action 系列无痕埋点

UIGestureRecognizer 和 UIControl 的埋点实现方案相同,此处仅介绍 UIButton 的无痕埋点。

在没有无痕埋点的情况下,UIViewController 和 UIButton 的持有关系如下:

2716570-0b789876a7295084.png
View Controller 和 Button 持有关系

同样,为了不修改 View Controller ,我们也添加了一个对象。但基于 Target-Action 是个集合的关系,这个对象并非中间对象,而是附属对象:

2716570-ab4c3372be2febf9.png
增加一个 Action 对象

大体步骤如下:

  1. 使用 runtime 对 - addTarget:action: 方法交换,插入捕获代码
  2. 当捕获到时,创建 Action 对象
  3. [target class]action 记录到 Action 对象中(当做埋点参数)
  4. 调用原方法,将 target 和 action 添加进 UIButton
  5. 调用原方法,将 Action 和 action: 添加进 UIButton

Action 对象实现大致如下:

- (void)action:(UIControl *)sender {
    // 埋点
}

接下来,当按钮产生事件后,会依次执行 View Controller 的代码和 Action 的代码,Action 则可以通过步骤 3 记录的数据来完成埋点。

为了防止 Action 对象销毁,我们需要拿其中一个对象关联住 Action,但是用 View Controller 还是 UIButton 来持有需要进行一系列的场景模拟。

当 UIButton 持有 Action 时:

2716570-0ffdb67847b39c2e.png
image.png

Button 先于 View Controller 销毁

Action 销毁,两处 target 持有关系断裂

View Controller 先于 Button 销毁

Action 没销毁,且依旧响应 UIButton 的点击事件(非预期效果)

当 View Controller 持有 Action 时:

2716570-3f20ed9fcfee3931.png
image.png

Button 先于 View Controller 销毁

Action 没销毁,两处 target 持有关系断裂

View Controller 先于 Button 销毁

Action 销毁,两处 target 持有关系断裂

通过模拟,发现使用 View Controller 持有 Action 对象更合适

接下来还有一些细节要处理:

  1. View Controller 可以接受多个 Button 的点击事件,所以关联对象的 Key 需要动态生成一个唯一 Key。可以通过 VC类名.控件类名.方法名.ControlEvent名 来生成 Key。
  2. UIControl 支持 remove 操作,所以也要 hook remove 方法,删除 Action 对象。
  3. UIGestureRecognizer 有 - initWithTarget:action: 方法,也需要被 hook,然后按照 - addTarget:action: 同样的方式处理
  4. Action 对象可以根据自己的埋点需求,通过属性来存储埋点时需要的参数。我们用 Action 记录了 VC 的类名以及方法名称,供生成埋点唯一 code。

解决的缺陷

现在再回来分析之前的缺陷是否还存在:

缺陷1(解决): 没有携带 sender 参数的 action 可能会导致手势埋点闪退

埋点和业务走两套 target-action,不会因为是否包含 sender 参数导致出错

缺陷2(解决): 手势埋点会遗漏使用 addTarget:action: 方法添加的回调

不管是手势还是控件,都可以埋到 addTarget:action: 的方法

缺陷3(解决): 当业务同时绑定手势和控件到同一个方法时可能会导致闪退

手势埋点和控件埋点的 Action 对象完全独立,互不干扰。自然不会因为 sender 参数类型不对导致闪退

缺陷4(解决): 如果缺陷 2 不闪退,UIControl 触发事件会导致两次埋点

手势埋点和控件埋点的 Action 对象完全独立,互不干扰。所以手势触发时不会执行按钮埋点,按钮触发时不会执行手势埋点。

缺陷5(解决): 如果业务手动调用 action 会导致不必要的埋点

手势埋点和控件埋点均没有修改业务代码,所以业务如果自己调用了 action 也不会触发埋点。

缺陷6(解决): 移除 target-action 后埋点依旧有效

增加对 remove 的监听,实现埋点和 target-action 同步增删。

总结

经过分析,最终产生的无痕埋点方案安全高效,没有大量的方法替换的操作,对象销毁后 AOP 环境也会跟着销毁,并且适应各种业务场景。

以上就是 iOS 端无痕埋点解决方案 AOP 部分的实现。

再接下来的篇幅中,将会介绍无痕埋点如何生成一个唯一事件标识,以及如何在无痕埋点中携带一些业务数据。

2018-04-12 18:00:53 qq_35544470 阅读数 228

数据埋点

以下是数据埋点的基础类型,供各位参考,初始化简单,内部发送url也写好,因工作需要暂时写了基础类型。个人建议使用“钩子”去做,维护单一plist即可,耦合度降低很多,有时间会继续更新。

DDYAnalyse SDK

简单易用的埋点SDK
初始化项目,在AppDelegate.m内

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法内添加以下任一发送策略的代码即可

初始化

1.发送策略:DDYDefault

    [DDYClick DDY_startWithConfigure:DDYConfigInstance];

2.发送策略:DDYSend_Interval

    DDYConfigInstance.ePolicy = DDYSend_Interval;
    [DDYClick DDY_setLogSendInterval:90];
    [DDYClick DDY_startWithConfigure:DDYConfigInstance];

3.发送策略:DDYSend_Count

    DDYConfigInstance.ePolicy = DDYSend_Count;
    [DDYClick DDY_startWithConfigure:DDYConfigInstance];

页面计时事件

1.页面开始计时的地方

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        
        [DDYClick DDY_beginLogPageView:@"page_id_4001"];
    }

2.页面计时结束的地方

attributes根据自己的埋点规则可自定义,也可为nil

    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
    
        [DDYClick DDY_endLogPageView:@"page_id_4001" attributes:@{DDY_pidKey:@"pid=1"
        ,DDY_permanent_idKey:@"API_id"
        }];
    }

页面点击事件

attributes根据自己的埋点规则可自定义,也可为nil

    [DDYClick DDY_event:@"eventId_4002" attributes:@{DDY_pageIdKey:@"pageId_1003"
    ,DDY_pidKey:@"pid=2378"
    ,DDY_linkurlKey:@"product://pid=40082"
    ,DDY_contentKey:@"floor=xxx#tab=xxx"
    ,DDY_expandKey:@"(1002||||floor=B版主题馆1-1)(||||)(||||)"
    ,DDY_permanent_idKey:@"API_id"
    }];

详见demo

iOS无埋点数据采集

阅读数 4446

数据采集

博文 来自: kaka_2928

iOS数据埋点

阅读数 24

没有更多推荐了,返回首页