文章

iOS埋点接入文档

NEStatistics

NEStatistics是严选使用的基于业务需求的打点记录用户APP操作访问行为的一块高性能数据采集模块。

1. 接入方法

可以通过CocoaPods来进行安装

Podfile文件上方加入私有Pod的源(建议放在官方源后面)

1
source 'https://*******.com/CocoaPods/Specs.git'

然后在Podfile文件添加如下,最新版本是2.5.2

1
pod 'NEStatistics', :git => 'git@git.mail.netease.com:yanxuan-app/NEStatistics.git',:tag => 2.5.2

使用地方引入头文件

1
#import <NEStatistics/NEStatistics.h>

建议在pch中全局添加

2. 埋点SDK代理实现

2.1 SDK基础配置

2.1.1 导入全局头文件

1
#import <NEStatistics/NEStatistics.h>

2.1.2 添加埋点SDK代理

主要实现协议NEStatisticsDelegate和埋点SDK的配置初始化。建议使用代理类来写,如严选实现单例代理类,YXStatisticsConfig

2.1.2.1 NEStatisticsDelegate协议实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@protocol NEStatisticsDelegate <NSObject>
@required
/// 返回当前显示的顶层页面的页面属性
- (NESPageScheme *)nesTopvcScheme;
/// 埋点数据上报接口
- (void)nesUploadData:(NSDictionary *)data complete:(void (^)(BOOL))complete;

@optional
/// 需要实时更新的外层数据,例如:账号信息,需要放在这里更新
- (void)nesLiveUpdateData;
/// 统计数据开始上传回调,累计的数据是按一个个包进行上报的,包中能包含的事件量eventUploadMax,这个方法是
/// 开始执行上报的时候触发的
- (void)nesUploadStart;
/// 通过埋点名,来获取埋点的abtest_dis信息
- (NSString *)nesABTInfoWithEventName:(NSString *)eventName;
- (NSString *)nesABTInfoWithPagename:(NSString *)pagename
                          moduleName:(NSString *)moduleName;
/// 获取当前的网络状态
- (NSInteger)nesNetworkReachabilityStatus;
@end

其中主要实现方法如下,其他为可选实现

  1. nesTopvcScheme,获取当前APP顶层页面的页面属性
1
2
3
4
5
6
7
8
9
  - (NESPageScheme *)nesTopvcScheme {
      UIViewController *topController = [self topmostViewController];
      NESPageScheme *topvcScheme = nil;
      if ([topController.class isSubclassOfClass:YXBaseController.class]) {
          topvcScheme = [(YXBaseController *)topController pageScheme];
      }
      return topvcScheme;
  }
  1. nesUploadData:complete:,数据上报接口

    1
    2
    3
    4
    
    - (void)nesUploadData:(NSDictionary *)data complete:(void (^)(BOOL))complete {
    		// TODO: 自行添加https接口进行数据上报
        complete(TRUE);
    }
    

2.1.3 在代理中完成SDK配置

这部分可参考Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[NESAdditionalLogger setBlock:^(int level, const char *fullpath, int line, const char *prefix, NSString *content) {
    // 注册日志输出
//  [DDLog yx_channelLog:level fullpath:fullpath line:line prefix:prefix content:content];
    NSLog(@"%@", content);
}];

#ifdef DEBUG
    NESConfig.sharedInstance.openRecordLog = YES; // 输出所有埋点日志
//  NESConfig.sharedInstance.onlyShowVCLog = YES; // 只输出view和click
//  NESConfig.sharedInstance.openReportLog = YES; // 输出上报的日志
//  NESConfig.sharedInstance.openCheck = YES; // 是否启动埋点本地检查
#endif


/// 发送时间间隔 默认10s
NESConfig.sharedInstance.updateSessionPeriod = 1;
/// 项目前缀
NESConfig.sharedInstance.projectPrefix = @"NE";
/// 系统版本号
NESConfig.sharedInstance.OS_V = [NSString stringWithFormat:@"ios%@", [[UIDevice currentDevice] systemVersion]];
/// app版本号
NESConfig.sharedInstance.app_v = [NSString stringWithFormat:@"DEMO%@/%@",
                                  [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"],
                                  [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];
/// 统计版本号
NESConfig.sharedInstance.YXS_v = @"1";
/// 渠道号
NESConfig.sharedInstance.ch_Id = @"APPStore";
/// 一次上报最多埋点数
NESConfig.sharedInstance.eventUploadMax = 100;
/// 手机品牌型号 iPhone 6s
NESConfig.sharedInstance.model_Id = @"Simulator";
/// 设备id
NESConfig.sharedInstance.uuid = @"933b6dced3dc479c5e21d1288c2261e5";
/// 主账号
NESConfig.sharedInstance.account = @"qiujunyun1@163.com";
/// abtest信息
NESConfig.sharedInstance.abtest_info = @"[\"YX_SUGGESTION_001-001\",\"YX_WKWEBVIEW-001\",\"YX_LOGIN_QQ-000\"]";
NESConfig.sharedInstance.skipMark = @[@"click_cart_order"];
NESConfig.sharedInstance.universalPage = @[@"tcatelev2"];
NESConfig.sharedInstance.idfa = @"";
NESConfig.sharedInstance.uploadAnchor = @[@"open_app",@"special_app_awake_from_h5"];

[NEStatistics sharedInstance].delegate = self;
[NEStatistics.sharedInstance recordFinishLaunching:@"open_app"];
[NEStatistics.sharedInstance recordEnterBackground:@"exit_app_tobackground"];
[NEStatistics.sharedInstance recordEnterForeground:@"open_app_toforeground"];
[NEStatistics.sharedInstance recordTerminate:@"exit_app"];
[NEStatistics.sharedInstance recordViewtime:@"special_default_durtime"];

DemoNEStatisticsConfig中,还有其他实现方法,比如:

  1. NESConfig.sharedInstance.schemePagenameDic的实现 {schemehost: pagename}

    schemePagenameDic会在页面属性赋值scheme的时候,寻找scheme.host的key-value关系,找到这个页面的pagename,并进行赋值,然后用来实现一个页面名为default的埋点,比如,click_default_button默认页按钮点击,这种自动将此埋点的pagename属性自动设置为当前页面的pagename

    项目开发中,最好埋点管理平台的pagename就应该同路由的host保持一致

    严选项目已经迭代很久,埋点管理平台问了实现三端的pagename保持一致,所以导致管理平台上的pagename和移动端的路由sheme.host并不一致

2.1.4 添加UIViewController的埋点属性代理

主要实现协议NEStatisticsVCProtocol中的schemeUrl,其他为可选实现,以下面的埋点例子为讲解进行方法实现后的效果演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
{
  "event_action": "view",
  "event_name": "view_newitem",
  "frompage": "yanxuan://commoditydetails?source_tracker_type=1&commodityid=100100",
  "is_return": 1,
  "locpage": "yanxuan://homepage_newarrival",
  "log_source": "app",
  "page_name": "newitem",
  "parameters": {
    "from": 2,
    "name": "测试"
    "extra": {
        "resource": {
            "taskId": "44352561",
            "materialId": "49011191",
            "taskType": 1,
            "resourcesId": 1,
            "crmUserGroupId": "0",
            "crmUserGroupName": "无分组",
            "materialName": "女式咖啡碳+5℃保暖内衣(上衣/裤子)",
            "itemId": "1164006",
            "materialType": "商品id",
            "itemIdList": [
                "1164006"
            ],
            " itemFrom,": "1-运营指定 2-算法推荐",
            "rcmdsort": "算法排序标识boolean值, 首焦模块中可标识是否候选队列推荐素材",
            "recommend": "算法推荐队列数据"
        },
        "modelType": 1,
        "abtest_dis":'10_11|12_13' #实验ID_方案ID(rdc或者rcmd来源的多个实验  "|" 分隔)
    }
  },
  "sequence": "v_1_1550734611837",
  "sessionid": "416f72acc696ccd92cae7ddf3ef51c71530243064347",
  "timestamp_a": 1530243349740,
  "abtest_info": "",
  "abtest_dis": "实验a_分组2|实验b_分组3",
  "mi": "",
  "ypm": {
      "sequence":"v_1_1550734611835",
      "page_name":"newitem",
      "event_name": "view_newitem",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
   },
  "ypm_list": [{
      "sequence":"v_1_1550734611835",
      "page_name":"index",
      "event_name": "view_index",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"v_1_1550734611835",
      "page_name":"index",
      "event_name": "click_index_new",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"v_1_1550734611835",
      "page_name":"newitem",
      "event_name": "view_newitem",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"v_1_1550734611835",
      "page_name":"newitem",
      "event_name": "click_newitem_allitem",
      "parameters": { "itemid": 100100 },
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"-1",
      "page_name":"",
      "event_name":"",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
        }
   ],
  "mark" : {
    "page": "index",
    "module": {
      "name":"click_index_lightsale",
      "parameters":{
        "sequen":1,
        "itemId":1098323
      },
          "abtest_dis":"实验a_分组2|实验b_分组3",
          "mi":""
    }
   }
}
2.1.4.1 NEStatisticsVCProtocol协议实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
@protocol NEStatisticsVCProtocol <NSObject>

@required
/**
 * @brief 主要必须实现方法,获取当前页面的scheme以及拼接参数
 *
 * schemeUrl标识一个页面的路由链接,但是很多情况会同时存在两个相同的页面,比如说两个相同商品的商品详情页
 * 这个时候就需要配合使用NESPageScheme的declareObj
 *
 * 存在之初是用来赋值locpage和frompage,算是第一阶段的路径追踪
 *
 * 下面的代码段演示一下schemeUrl在严选项目中的赋值逻辑,当然也可以结合自己的项目单独设计
 * @code
 - (NSString *)schemeUrl {
     NSMutableString *scheme = [NSMutableString string];
     // 统一获取页面的路由scheme的接口
     if ([self.class respondsToSelector:@selector(urlPath)]) {
         [scheme appendFormat:@"%@?",[self.class performSelector:@selector(urlPath)]];
     }
     // 是否需要额外参数区分两个页面,这里是通过属性的方式拼接
     if ([self.nessource yx_isValidString]) {
         [scheme appendFormat:@"%@&", self.nessource];
     }
     
     // webview 单独处理 获取webview上面的statisticsUrl属性
     if ([self isKindOfClass:YXWebViewController.class]) {
         scheme = [@"yanxuan://yxwebview?" mutableCopy];
         if ([self.nessource yx_isValidString]) {
             [scheme appendFormat:@"%@&", self.nessource];
         }
         
         NSString *url = [self valueForKey:@"url"];
         if ([url yx_isValidString]) {
             [scheme appendFormat:@"url=%@", url];
             return scheme;
         }
     }
         
     /// 拼接路由参数
     if (self.routeParams) {
         [self.routeParams enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
             // 只接受string和number的参数
             if ([obj yx_isValidString] || [obj isKindOfClass:NSNumber.class]) {
                 [scheme appendFormat:@"%@=%@&",key , obj];
             }
         }];
     }
     /// 拼接vc的自定义参数
     if ([self respondsToSelector:@selector(statisticsScheme:)]) {
         scheme = [[(id<NEStatisticsVCProtocol>)self statisticsScheme:scheme] mutableCopy];
     }
     
     return scheme;
 }
 *
 */
@property (nonatomic, strong) NSString *schemeUrl;

@optional

/**
 * @brief 每个页面的页面属性
 *
 * schemeUrl和pageScheme的含义理解是,schemeUrl是简单字符串类型的页面标识,pageScheme则是面向对象类型的页面标识
 *
 * pageScheme中包含有schemeUrl的属性,他的存在目的是为了完成schemeUrl无法完成的页面透传和入口页方案,实现第二阶段的路径追踪能力
 */
@property (nonatomic, strong) NESPageScheme *pageScheme;

/**
 * @brief 用于跳过第一次的访问统计,使用此参数对应的KEY注意跳过之后该KEY对应的value赋值
 *
 * 使用场景:
 *
 * 1. 页面访问统计需要当前页面的接口返回的数据的时候。
 * 访问统计必须在viewdidappear中进行的。(因为locpage frompage刷新是在基类viewdidappear)
 * 页面数据request请求快慢不定,会在viewdidappear前后请求回来。
 * 示例见 view_detail
 *
 * 2. pagecontroller的时候
 * 第一次进入到页面会有loadpage 这个时候可用于标志位跳过第一次的viewdidappear
 * 示例见 view_orderlist
 *
 * 设计这个在属性的目的,是为了灵活处理各种业务场景的bool值控制需要,这样就不用每个地方都建立一个bool,可以统一用字典管理
 * 下面代码段演示的是二级分类页中,其中l2CategoryId是当前页面的接口请求返回的
 * @code
 - (void)statisticsView {
     if ([[self.hasStatistics objectForKey:YXS_VIEW_CATELEV2] boolValue]) {
         if (self.l2CategoryId) {
             [NEStatistics.sharedInstance refreshPage];
             [NEStatistics.sharedInstance recordEvent:YXS_VIEW_CATELEV2
                                           parameters:@{@"categoryId":@(self.l2CategoryId),
                                                        @"superCategoryId":@(self.l1CategoryId)}];
         }
     }
     [self.hasStatistics setObject:@(YES) forKey:YXS_VIEW_CATELEV2];
 }
 */
@property (nonatomic, strong) NSMutableDictionary *hasStatistics;

/**
 * @brief 通过属性赋值的方式,将存在两个页面是相同的sheme,区分开
 *
 * 通过source参数,来增加scheme参数进行区分,例如:
 *
 * 首页tab福利社和福利社落地页的scheme都是如下,取值nessource为_nestat_s=indextab
 *
 * yanxuan://yxwebview?url=https://m.you.163.com/saleCenter/index
 *
 * 改动之后:
 *
 * yanxuan://yxwebview?_nestat_s=indextab&url=https://m.you.163.com/saleCenter/index
 *
 * 注意拼接的位子,可选使用参数,赋值之后需要在schemeUrl的get方法中获取使用即可
 *
 * 下面代码段,演示nessource赋值逻辑
 * @code
 NSInteger currentPage = _pageControllerView.currentPageIndex;
 YXBaseController *vc = (YXBaseController *)[_pageControllerView pageControllerView:currentPage];
 YXHomepageSegmentModel *item = [self.allSegmentItems at:currentPage];
 if (item.type == YXHomepageSegmentType_limit ||
     item.type == YXHomepageSegmentType_new ||
     item.type == YXHomepageSegmentType_saleCenter) {
     vc.nessource = @"_nestat_s=indextab";
 }
 *
 */
@property (nonatomic, strong) NSString *nessource;

/**
 * @brief 页面访问埋点核心触发方法,可选实现
 *
 * 1. 严选中实现是在基类中兜底实现view_default,如果子类没有或是忘记实现访问埋点,则会走到基类中触发兜底访问
 *
 * 2. 未实现此方法,则自动读取schemeUrl中的path,拼接成view_abc,例如:schemeUrl = yanxuan://abc?parama=b
 *
 * @code
 - (void)statisticsViewLaunch {
     [NEStatistics.sharedInstance recordEvent:YXS_VIEW_ABOUTUS];
 }
 */
- (void)statisticsViewLaunch;

/*!
 * @brief 通过协议方法实现的方式,定制页面的scheme
 * @param scheme 当前scheme
 *
 * @return 返回自定义拼接参数后的scheme
 *
 * 下面代码段实现样例,比如商品分类页面,类目id是不一样的,但是其他scheme都是一样的,可以在类目vc中单独拼接id参数
 * @code
 #pragma mark - NEStatisticsVCProtocol
 - (NSString *)statisticsScheme:(NSString *)scheme {
     NSMutableString *schemeUrl = [NSMutableString stringWithString:scheme];
     [schemeUrl appendFormat:@"categoryL1=%ld",self.l1Id];
     return schemeUrl;
 }
 */
- (NSString *)statisticsScheme:(NSString *)scheme;
@end
2.1.4.2 页面路由配置为埋点页面schemeUrl

建议在controller的统一基类中进行配置schemeUrl

1
2
3
4
5
6
7
8
9
// BaseViewController.m
- (NSString *)schemeUrl {
    NSMutableString *scheme = [NSMutableString string];
    // urlPath为页面路由的scheme
    if ([self.class respondsToSelector:@selector(urlPath)]) {
        [scheme appendFormat:@"%@?",[self.class performSelector:@selector(urlPath)]];
    }
    return scheme;
}

完成以上配置即可达到埋点基础字段的frompagelocpageypmypm_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
·
·
"frompage": "yanxuan://commoditydetails?source_tracker_type=1&commodityid=100100",
"locpage": "yanxuan://homepage_newarrival",
·
·
"ypm": {
      "sequence":"v_1_1550734611835",
      "page_name":"newitem",
      "event_name": "view_newitem",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
   },
  "ypm_list": [{
      "sequence":"v_1_1550734611835",
      "page_name":"index",
      "event_name": "view_index",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"v_1_1550734611835",
      "page_name":"index",
      "event_name": "click_index_new",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"v_1_1550734611835",
      "page_name":"newitem",
      "event_name": "view_newitem",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"v_1_1550734611835",
      "page_name":"newitem",
      "event_name": "click_newitem_allitem",
      "parameters": { "itemid": 100100 },
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
      },{
      "sequence":"-1",
      "page_name":"",
      "event_name":"",
      "parameters":"",
      "abtest_dis":"实验a_分组2|实验b_分组3",
      "mi":""
        }
   ],
   ·
   ·

非入口页的路径透传方案,详细见方案

2.1.4.3 配置项目中页面的入口页

实现NEStatisticsVCProtocol中的pageScheme,如:

1
2
3
4
5
// HomeViewController.m
- (NESPageScheme *)pageScheme {
    [super.pageScheme loadMark];
    return super.pageScheme;
}

重写pageScheme的get方法,标记首页为入口页,这样从首页进入到的后续非入口页的页面里面触发的埋点,都会带上首页mark

1
2
3
4
5
6
7
8
9
10
11
12
"mark" : {
    "page": "index",
    "module": {
      "name":"click_index_lightsale",
      "parameters":{
        "sequen":1,
        "itemId":1098323
      },
          "abtest_dis":"实验a_分组2|实验b_分组3",
          "mi":""
    }
   }
2.1.4.4 其他

其他协议可参考注释自由选择实现

2.2 使用SDK进行打点

本地埋点管理有两种方式可以进行,严选的埋点管理平台见

  1. plist文件统一管理所有埋点

    优点:plist文件会使用py脚本自动生成字符串常量,在调用的时候可以灵活的使用代码提示,集中管理

    缺点:多人协作的时候,plist文件容易冲突,基本每次改动合并主分支都需要手动解决冲突

  2. 直接代码打入埋点

    优点:灵活,分散管理,跟随代码自动删除下线,无多人协作的冲突问题

    缺点:埋点key需要手动输入,容易输错

    配合网易严选的埋点管理平台,集成了埋点代码块生成能力,有效解决了此问题,此部分需要配置埋点管理平台进行,如果有代码段能力,推荐使用方法2进行本地代码管理

2.2.1 plist方法进行埋点管理试打点

iOS统计模块的埋点键值是通过python脚本编辑plist配置文件,自动生成字符串常量的.h和.m文件。

2.2.1.1 配置Python脚本

Targets > NEStatisticsDemo > Build Phases > add a new build phase > 输入statisticsScript.py的位置

1
/usr/bin/python ./Pods/NEStatistics/NEStatistics/Auto/statisticsScript.py

然后新建的phase记得放在Compile Sources前面。

2.2.1.2 配置脚本编译开关

在系统的三类(如果有三类).xcconfig文件中配置如下

AppStore.xcconfig

1
NES_UPDATE_ENABLE = NO

Debug.xcconfig

1
NES_UPDATE_ENABLE = YES

Release.xcconfig

1
NES_UPDATE_ENABLE = NO
2.2.1.3 建立埋点key配置表NESEventProperties.plist文件
key类型必填备注 
event_nameNSString埋点事件名(eventaction_pagename_event) 
event_actionNSString埋点事件动作类型 click view special(如果有此项或是值为空,就不会取规范命名event_name的eventaction,反之一样如果需要自定义可以增加此项) 
page_nameNSString埋点所在页面名(如果有此项或是值为空,就不会取规范命名event_name的pagename,反之一样如果需要自定义可以增加此项) 
locpageNSString当前埋点页面的schemeUrl 
recordtopageBOOL是否需要记录下一个页面的locpage 
ignorefrompageBOOL是否忽略不记录frompage 例如view_launchpage 
parameterstypeEnum NSInteger需统一添加的参数类型,目前用到的类型就是1 首页的埋点userType:是否新人标识 
descriptionString该埋点的需求描述文案 
versionString该埋点埋入的app版本号 
dateString埋点记录时间 
paramsString埋点参数注释 

plist配置表,一项就是一个埋点。例如:

key类型value 
event_nameStringclick_demopage_testaction 
event_actionStringclick 
page_nameStringdemopage 
locpageString  
recordtopageBooleanNO 
parameterstypeNumber1 
ignorefrompageBooleanNO 
descriptionString测试页面的点击按钮点击(具体属性注释可以看YXSEventProperty) 
versionString1.0.0 
authorStringqiujunyun 
dateString2018-03-06 
paramsStringurl: NSString 

编译之后就会在同目录下生成.h和.m文件,并拖到项目中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// YXStatisticsHeader.m
/*!
 * @breif 测试页面 点击按钮点击(具体属性注释可以看NESEventProperty)
 *
 *       params: url: NSString
 *
 *    page_name: demopage
 *
 * event_action: click
 *
 *parameterstype: 需要底层添加userType参数。
 *
 * @author qiujunyun
 * @version 1.0.0
 * @date 2018-03-06
 */ 
UIKIT_EXTERN NSString *const NES_CLICK_DEMOPAGE_TESTACTION;
1
2
/// YXStatisticsHeader.m
NSString *const NES_CLICK_DEMOPAGE_TESTACTION                          = @"click_demopage_testaction";

精简版只需要配置event_name、topagetype、description即可

2.2.1.4 使用埋点
1
2
3
4
/// ButtonViewController
- (IBAction)clickAction:(id)sender {
    [NEStatistics.sharedInstance recordEvent:NES_CLICK_DEMOPAGE_TESTACTION];   
}

2.2.2 直接代码打入埋点

直接使用埋点SDK调用接口打点,如下:

1
[NEStatistics.sharedInstance recordTK:@"click_tab"];

详细接口见NEStatistics.h头文件,主要介绍四个方法

1
2
3
4
5
6
7
8
9
/// 新命名 指定key的不记录topage埋点方法
- (void)recordTK:(NSString *)key;
- (void)recordTK:(NSString *)key
          params:(NSDictionary *)parameters;

/// 新命名 指定key的不记录topage埋点方法
- (void)recordNTK:(NSString *)key;
- (void)recordNTK:(NSString *)key
           params:(NSDictionary *)parameters;

埋点事件主要分为四类:view(页面访问)、show(模块曝光)、click(模块点击)、special(其他事件)

其中点击埋点是需要区分,此次点击是否会跳转页面,所以有两种,在plist埋点的配置中是recordtopage进行配置管理

在埋点方法中则是recordTK:recordNTK:这种进行区分

1
2
[NEStatistics.sharedInstance recordTK:@"click_default_button"];
[NEStatistics.sharedInstance recordNTK:@"show_default_button"];

点击会跳转页面的这种称为:带跳转的点击埋点,这类埋点的表现就是

  1. 当前埋点的基础字段的topage会有值

    1
    
    "topage": "yanxuan://homepage_newarrival",
    
  2. 入口页方案非入口页方案中,很重要

    只有带跳转的点击才认为是需要透传的点击,不然在ypm_list中会用-1代替,mark中的module会为空

配合埋点管理平台使用代码段能力

夸父代码段

点击即可生成埋点代码段,直接在需要打点的进行打点即可

2.3 进入一个已经存在的页面,认为是后退访问

网易严选项目,认为后退访问是一个无效的访问,会在访问埋点上面打上一个标识位,见埋点字段is_return

1
"is_return" = 1,

我们定义:第二次进入一个已经存在的页面就是后退访问,除去tabcontroller一类左右多tab页面切换

访问埋点是在页面生命流程的viewDidAppear:中触发的,is_return的逻辑也已经很好的兼容了,其中需要额外处理的是手势后退的兼容,考虑手势后退一半又取消后退了的情况下也会触发viewDidAppear:的问题,这种情况不应该触发访问埋点,需要在项目中进行配置,能做到手势后退一半又取消后退,见视频操作。

在自定义的后退手势中,获取到取消后退的节点,并调用

1
[NEStatistics.sharedInstance navControllerBackPanGestureHandlerReceiveCancel:isCanceled];

详细可以参考Demo中BaseViewController.m的演示

2.4 自定义的tabController式的多标签页的页面

简单来讲这种页面就是一个UIViewController中包含多个子页面的形式,子页面也是使用Controller来实现的,如下

screenshot-20210621-165142

四个页面的切换是不涉及push和pop的那种的,所以需要定制,详细见DemoTabMoreViewController

  1. 复写pageScheme,将四个页面的scheme定义为四个不同的scheme,也可以通过path不同来区分,主要是区分locpage
  2. 实现statisticsViewLaunch,然后自定义访问埋点
  3. 在segment和controller切换的时候主动触发访问埋点
  4. 记得处理当前的页面访问是否是回退访问逻辑,因为子Controller可能会触发viewDidAppear导致

2.5 日志输出配置

1
2
3
4
5
6
[NESAdditionalLogger setBlock:^(int level, const char *fullpath, int line, const char *prefix, NSString *content) {
// 注册日志输出
//                [DDLog yx_channelLog:level fullpath:fullpath line:line prefix:prefix content:content];
	NSLog(@"%@", content);
}];

2.6 埋点相关的日志输出控制

1
2
3
4
5
6
#ifdef DEBUG
            NESConfig.sharedInstance.openRecordLog = YES; // 输出所有埋点日志
//            NESConfig.sharedInstance.onlyShowVCLog = YES; // 只输出view和click
//            NESConfig.sharedInstance.openReportLog = YES; // 输出上报的日志
//            NESConfig.sharedInstance.openCheck = YES; // 是否启动埋点本地检查
#endif

2.7 本地埋点可视化工具

实现如下方法即可

1
[NESAssistiveManager.shareInstance show];

screenshot-20210621-172622

Demo

Demo中包含完整的实现,可以参考实现

本文由作者按照 CC BY 4.0 进行授权