iOS 無痕埋點方案及實戰分享

前言

  • 所謂埋點就是在應用中特定的流程收集一些信息,用來跟蹤應用使用的情況,隨着如今的互聯網的愈來愈便利,而後精準分析用戶數據變成爲一種新的大趨勢,用戶在這個頁面停留時間、點擊按鈕、瀏覽內容、手機型號、網絡環境等等等均可以進行統計

要作數據分析不外乎就兩種,一種服務器經過接口調用狀況統計,另一種就是前端埋點統計,固然前端埋點統計能夠更精準的統計更多數據信息~html

埋點方案

大體分爲三種,代碼埋點、可視化埋點,無埋點前端

第一種:代碼埋點

很簡單明瞭就是在須要統計埋點的控制器和按鈕事件等地方作好埋點處理git

第二種:可視化埋點

根據標識來識別每個事件, 針對指定的事件進行取參埋點。而事件的標識與參數信息都寫在配置表中,經過動態下發配置表來實現埋點統計github

第三種:無埋點

無埋點是指開發人員集成採集 SDK 後,SDK 便直接開始捕捉和監測用戶在應用裏的全部行爲,並所有上報,不須要開發人員添加額外代碼json

想關於這三種的介紹,網上資料一大堆,感興趣的朋友能夠本身去搜索看看,安全

美團點評前端無痕埋點實踐服務器

實戰處理

統計用戶瀏覽頁面

下面是一個很簡單的統計用戶瀏覽埋點方式,其實就是利用Runtime交換viewDidLoad方法markdown

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        kMethodSwizzling(self.class, @selector(viewDidLoad), @selector(kj_viewDidLoad));
    });
}
- (void)kj_viewDidLoad{
    [self kj_viewDidLoad];
    NSString *clazz = NSStringFromClass([self class]);
    NSDictionary *dict = @{
        @"userid":KJHookInfo.shared.userid,
        @"viewController":clazz
    };
    NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key");
    //TODO:上傳數據
}
複製代碼

統計指定頁面

升級一下,瀏覽指定控制器埋點,經過接口動態返回須要統計的界面控制器,儲存在hookViewControllers,而後統計網絡

- (void)kj_viewDidLoad{
    [self kj_viewDidLoad];
    NSString *clazz = NSStringFromClass([self class]);
    BOOL isHook = ({
        BOOL isHook = NO;
        for (NSString *name in KJHookInfo.shared.hookViewControllers) {
            if ([name isEqualToString:clazz]) {
                isHook = YES;
                break;
            }
        }
        isHook;
    });
    if (isHook) {
        NSDictionary *dict = @{
            @"userid":KJHookInfo.shared.userid,
            @"viewController":clazz
        };
        NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key");
        //TODO:上傳數據
    }
}
複製代碼

統計用戶頁面瀏覽時長

再介紹一個統計頁面停留時長的埋點處理 簡單講就是交換viewWillAppear:viewDidDisappear:app

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        kMethodSwizzling(self.class, @selector(viewWillAppear:), @selector(kj_viewWillAppear:));
        kMethodSwizzling(self.class, @selector(viewDidDisappear:), @selector(kj_viewDidDisappear:));
    });
}
- (void)kj_viewWillAppear:(BOOL)animated{
    [self kj_viewWillAppear: animated];
    // 記錄進入時間
    KJHookInfo.shared.time = CFAbsoluteTimeGetCurrent();
}
- (void)kj_viewDidDisappear:(BOOL)animated{
    [self kj_viewDidDisappear: animated];
    NSString *clazz = NSStringFromClass([self class]);
    BOOL isHook = ({
        BOOL isHook = NO;
        for (NSString *name in KJHookInfo.shared.hookViewControllers) {
            if ([name isEqualToString:clazz]) {
                isHook = YES;
                break;
            }
        }
        isHook;
    });
    if (isHook) {
        NSTimeInterval time = CFAbsoluteTimeGetCurrent() - KJHookInfo.shared.time;
        NSDictionary *dict = @{
            @"userid":KJHookInfo.shared.userid,
            @"time":time,
            @"viewController":clazz
        };
        NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key");
        //TODO:上傳數據
    }
}
複製代碼

用戶點擊事件

大多數可點擊UI控件都是基於UIControl,核心仍是交互方法sendAction:to:forEvent:,下面提供一種統計到某個具體頁面的按鈕

@implementation UIControl (KJHook)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{    
        kMethodSwizzling(self.class, @selector(sendAction:to:forEvent:), @selector(kj_sendAction:to:forEvent:));
    });
}
- (void)kj_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event {
    [self kj_sendAction:action to:target forEvent:event];
    _weakself;
    kGCD_async(^{
        if ([NSStringFromClass(weakself.class) isEqualToString:@"UIButton"]) {
            void (^kDealButton)(NSString *) = ^(NSString *clazz){
                NSDictionary *dict = @{
                            @"userid":@"userid",
                            @"centerX":[NSString stringWithFormat:@"%.2f",weakself.centerX],
                            @"centerY":[NSString stringWithFormat:@"%.2f",weakself.centerY],
                            @"viewController":clazz
                        };
                NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key");
                NSLog(@"%@",parameter);
                //TODO:上傳數據
            };
            kGCD_main(^{
                kDealButton(NSStringFromClass([[target viewController] class]));
            });
        }
    });
}
@end
複製代碼

這裏若是想更準確,還可給每一個按鈕編tag值,固然這個工程量就比較大,這邊只是提供思路,具體怎麼作示實際狀況爲準

這裏值得一提的就是,在交換方法的時候,必定要全局查詢是否命名衝突(有沒有方法名重複),不然可能會出現你埋點的方法壓根不執行,原理能夠參考我另一篇關於Category介紹的文章iOS Category類別的使用及工具封裝

埋點後續處理

接口數據上傳

一般咱們埋好點以後,採起的方式都是調用服務器的一個指定接口,可是有一個缺陷就是在高峯期時刻訪問量會很是巨大,就有超出服務器範圍的可能

圖片訪問式統計

先來介紹網址連接的構成,https://upload-images.jianshu.io/upload_images/1933747-82138031f05852ab.gif?tR8XBkv3BaBjjEeck9VbeiZauP73MdXWlhvmUq+BAFY=

scheme:https  
host:upload-images.jianshu.io
path:/upload_images/1933747-82138031f05852ab.gif
query:tR8XBkv3BaBjjEeck9VbeiZauP73MdXWlhvmUq+BAFY=
複製代碼

正常咱們訪問一張圖片,其實只須要scheme:// + host + path 三部分就能夠正常訪問

可是下面這張格式組成 scheme:// + host + path + ? + query 也是能夠正常訪問

因此咱們能夠將埋點信息放在query當中,而後只須要統計這張圖片的訪問記錄就能夠簡單快捷的拿到埋點的內容

  • 帶寬考慮:圖片咱們也能夠採用1像素的圖片,這樣也不會怎麼佔用服務器的帶寬
  • 安全考慮:一樣咱們也能夠將須要的參數加密
NSDictionary *dict = @{@"app":@"appname"};
NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key");
複製代碼

一樣也是能夠訪問到圖片地址 https://upload-images.jianshu.io/upload_images/1933747-82138031f05852ab.gif?tR8XBkv3BaBjjEeck9VbeiZauP73MdXWlhvmUq+BAFY=

後續思考

咱們都知道訪問獲得的圖片都是data數據,那麼咱們是否也能夠把咱們想要反饋給客戶端的數據藏於data當中解析呢?

那麼你們還有什麼比較優秀的埋點方式呢?歡迎分享,謝謝~

備註:本文用到的部分函數方法和Demo,均來自三方庫**KJEmitterView**,若有須要的朋友可自行pod 'KJEmitterView'引入便可

無痕埋點方案及實戰分享介紹就到此完畢,後面有相關再補充,寫文章不容易,還請點個**小星星**傳送門

相關文章
相關標籤/搜索