第47月第21天 iOS 无痕埋点解决方案

1.

生成 Target-Action 埋点的唯一 ID

Target-Action 是手势和 UIControl 的回调,一般使用如下代码

  1. -[UIControl addTarget:action:events:]
  2. -[UIGestureRecognizer initWithTarget:action:]

我们 hook 了 -[UIControl addTarget:action:events:] 方法,创建了一个 Action 对象作为附属对象,和实际的 target 一同添加到 UIControl 中。当 UIControl 触发了事件,就会同时向业务对象和 Action 对象发送消息,从而产生埋点。故我们可以在 -[UIControl addTarget:action:events:] 方法中获取到 target、action、event。还能从 -[Action action:] 方法中获取实时埋点数据。

采集

-[UIControl addTarget:action:events:]

在此方法中,我们可以获取到 UIControl 类名,target 对象,action 方法名,events 事件名。由于后三个数据在下面的方法中无法获取,所以会记录后三个数据到 Action 对象中,供 Action 对象在触发下面的方法时获取对应数据:

//  In UIControl+Hook.m

- (void)hook_addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)events {

    // Call origin method
    [self hook_addTarget:target action:action forControlEvents:events];
    
    //  Create Action object
    MyTargetAction *action = [[MyTargetAction alloc] init];
    action.targetName = NSStringFromClass([target class]);
    action.action = NSStringFromSelector(action);
    action.events = events;

    // Add Action object
    [self hook_addTarget:action action:@selector(action:) forControlEvents:events];
}

-[Action action:]

此方法是实际埋点执行的方法,由于此方法只能获取 self(Action 对象)和 sender(UIControl 对象),故实际埋点数据还是依赖前一个方法临时保存的数据。

此时我们可以在方法中构成埋点数据。

  • self(Action 对象)
  • sender(UIControl 对象)
    • VC(可以根据 UIControl 获取所在 VC)
  • self.targetName(target 类名)
  • self.action(action 方法名)
  • self.events(events 值)

拼接事件

Action 对象

此对象是 SDK 内部对象,无任何信息

sender

控件对象,大部分按钮不会继承,所以也不会有信息。

self.targetName

响应者类名,此类一般为 VC 的类名,或者某个 View 的类名,故此信息可用于拼接。

self.action

响应方法名,于前一个相同,但不同事件一般会有不同方法回调,所以方法名也可以作为唯一事件 ID。

self.events

事件类型,不同控件不同事件,但按钮基本为 UIControlEventsTouchUpInside,如果要区分不同控件则可以加入,本文只考虑按钮情况。故不加入此信息

最终事件 ID 如下:

VCClassName#TargetClassName#ActionName

//  In MyTargetAction.m

- (void)action:(UIControl *)sender {
    NSString *event_code = ({
        NSString *viewController = ({
            UIResponder *responder = sender;
            while (responder) {
                responder = responder.nextResponder;
                if ([responder isKindOfClass:[UIViewController class]]) {
                    break;
                } else if ([responder isKindeOfClass:[UIWindow class]]) {
                    break;
                }
            }
            NSStringFromClass([responder class]);
        });
        [NSString stringWithFormat:@"%@#%@#%@", viewController, self.targetName, self.actionName];
    });
        
    [Tracker trackEvent:event_code];
}

举例:

UIViewController#UIViewController#onClick:

UIViewController#MenuItemCell#onClickAdd:




 

https://www.jianshu.com/p/f3d5e7beca23

posted @ 2020-08-21 17:43  lianhuaren  阅读(295)  评论(0编辑  收藏  举报