統(tǒng)計分析管理類嘿歌,包含對所有ViewController添加頁面統(tǒng)計方法烟很、對單一ViewController添加頁面統(tǒng)計方法以及手動添加事件的相關(guān)方法跃洛。需要添加友盟庫
#import <UIKit/UIKit.h>
@interface RSAppAnalyticsManager : NSObject
/**
集成測試設(shè)備id此迅,測試階段需在UM平臺->管理->集成測試中添加deviceId
@return UM deviceId
*/
+ (NSString*)RS_debugDeviceId;
/**
初始化統(tǒng)計功能
@param appKey UM appKey
@param channel 頻道(nil:App Store)
@param open 是否打開崩潰信息收集
*/
+ (void)RS_initAppAnalyticsWithAppKey:(NSString*)appKey channel:(NSString *)channel openCrashReport:(BOOL)open;
/**
對指定控制器添加計算統(tǒng)計
---*********注意*不可與全局頁面計算統(tǒng)計功能(RS_addAnalyticsForAllViewController)同時使用盗蟆,會造成死循環(huán)*********-----
@param viewController 目標控制器
*/
+ (void)RS_addAnalyticsForViewController:(UIViewController*)viewController;
/**
添加全局ViewController頁面計算統(tǒng)計
*/
+(void)RS_addAnalyticsForAllViewController;
/**
是否開啟日志功能(release下自動設(shè)置NO)
@param enabled 是否打開日志功能勺爱;debug狀態(tài)有效
*/
+ (void)RS_setLogEnabled:(BOOL)enabled;
/*----------------------計數(shù)事件-----------------------*/
+ (void)RS_event:(NSString *)eventId;
+ (void)RS_event:(NSString *)eventId label:(NSString *)label;
+ (void)RS_event:(NSString *)eventId attributes:(NSDictionary *)attributes;
+ (void)RS_event:(NSString *)eventId attributes:(NSDictionary *)attributes counter:(int)number;
/*----------------------計算事件-----------------------*/
+ (void)RS_beginEvent:(NSString *)eventId;
+ (void)RS_endEvent:(NSString *)eventId;
+ (void)RS_beginEvent:(NSString *)eventId label:(NSString *)label;
+ (void)RS_endEvent:(NSString *)eventId label:(NSString *)label;
+ (void)RS_beginEvent:(NSString *)eventId primarykey :(NSString *)keyName attributes:(NSDictionary *)attributes;
+ (void)RS_endEvent:(NSString *)eventId primarykey:(NSString *)keyName;
+ (void)RS_event:(NSString *)eventId durations:(int)millisecond;
+ (void)RS_event:(NSString *)eventId label:(NSString *)label durations:(int)millisecond;
+ (void)RS_event:(NSString *)eventId attributes:(NSDictionary *)attributes durations:(int)millisecond;
/*----------------------頁面統(tǒng)計-----------------------*/
+ (void)RS_logPageView:(NSString *)pageName seconds:(int)seconds;
+ (void)RS_beginLogPageView:(NSString *)pageName;
+ (void)RS_endLogPageView:(NSString *)pageName;
@end
#import "RSAppAnalyticsManager.h"
#import <UMCommon/UMCommon.h>
#import <UMAnalytics/MobClick.h>
#ifdef DEBUG
#import <UMCommonLog/UMCommonLogHeaders.h>
#endif
#import <objc/message.h>
static NSString* const kSUBCLASS_PREFIX_NAME = @"RSAnalytics_Target_ViewController";
@implementation RSAppAnalyticsManager
#pragma mark - public
+ (NSString*)RS_debugDeviceId{
return [UMConfigure deviceIDForIntegration];
}
+ (void)RS_initAppAnalyticsWithAppKey:(NSString*)appKey channel:(NSString *)channel openCrashReport:(BOOL)open{
//開啟統(tǒng)計數(shù)據(jù)加密傳輸
[UMConfigure setEncryptEnabled:YES];
[MobClick setCrashReportEnabled:open];
[UMConfigure initWithAppkey:appKey channel:channel];
}
+ (void)RS_addAnalyticsForViewController:(UIViewController*)viewController{
if (viewController == nil || ![viewController isKindOfClass:[UIViewController class]]) {
return;
}
NSString* className = NSStringFromClass([viewController class]);
NSString* subClassName = [kSUBCLASS_PREFIX_NAME stringByAppendingString:className];
Class subClass = NSClassFromString(subClassName);
if (subClass == nil) {
/*創(chuàng)建子類晃琳,在子類中添加方法下面兩個方法,確彼雎常控制器實現(xiàn)了這兩個方法*/
subClass = objc_allocateClassPair([viewController class], subClassName.UTF8String, 0);
objc_registerClassPair(subClass);
/*向子類中添加viewWillAppear:和viewWillDisappear:方法卫旱,防止jviewController中未實現(xiàn)這兩個方法,而導(dǎo)致接下來交換方法實現(xiàn)交換成UIViewController的兩個方法围段,如果交換的是UIViewController的兩個方法顾翼,則會作用全局,并且全局中其他控制器沒有交換到先添加的方法奈泪,導(dǎo)致程序奔潰*/
[self _RS_addViewWillAppearAndDisappearForSubClass:subClass formViewController:viewController];
}
//修改viewController的isa指針
object_setClass(viewController, subClass);
SEL rsViewWillAppearSEL = @selector(_RSAnalytics_viewWillAppear:);
///獲取IMP的兩種方法
//1. Method rsViewWillAppear = class_getInstanceMethod([self class], rsViewWillAppearSEL);
//IMP rsIMP = method_getImplementation(rsViewWillAppear);
//2. IMP rsIMP = class_getMethodImplementation([self class], rsViewWillAppearSEL)
if (class_addMethod([viewController class], rsViewWillAppearSEL, class_getMethodImplementation([self class], rsViewWillAppearSEL), "v@:B")) {
Method viewWillAppear = class_getInstanceMethod([viewController class],@selector(viewWillAppear:));
Method newWillAppearMethod = class_getInstanceMethod([viewController class], rsViewWillAppearSEL);
method_exchangeImplementations(viewWillAppear, newWillAppearMethod);
}
SEL rsviewWillDisappearSel = @selector(_RSAnalytics_viewWillDisappear:);
if (class_addMethod([viewController class], rsviewWillDisappearSel, class_getMethodImplementation([self class], rsviewWillDisappearSel), "v@:B")) {
Method viewWllDisappear = class_getInstanceMethod([viewController class], @selector(viewWillDisappear:));
Method newWillDisappearrMethod = class_getInstanceMethod([viewController class], rsviewWillDisappearSel);
method_exchangeImplementations(viewWllDisappear, newWillDisappearrMethod);
}
}
//依賴UIViewController+Analytics分類
+ (void)RS_addAnalyticsForAllViewController{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method viewWillAppear = class_getInstanceMethod([UIViewController class],@selector(viewWillAppear:));
Method rsViewWillAppear = class_getInstanceMethod([UIViewController class], @selector(_RSAnalytics_viewWillAppear:));
method_exchangeImplementations(viewWillAppear,rsViewWillAppear);
Method viewWllDisappear = class_getInstanceMethod([UIViewController class], @selector(viewWillDisappear:));
Method rsViewWillDisappear = class_getInstanceMethod([UIViewController class],@selector(_RSAnalytics_viewWillDisappear:));
method_exchangeImplementations(viewWllDisappear, rsViewWillDisappear);
});
}
+ (void)RS_setLogEnabled:(BOOL)enabled{
#ifdef DEBUG
[UMCommonLogManager setUpUMCommonLogManager];
[UMConfigure setLogEnabled:enabled];
#endif
}
/*----------------------計數(shù)事件-----------------------*/
+ (void)RS_event:(NSString *)eventId{
[MobClick event:eventId];
}
+ (void)RS_event:(NSString *)eventId label:(NSString *)label{
[MobClick event:eventId label:label];
}
+ (void)RS_event:(NSString *)eventId attributes:(NSDictionary *)attributes{
[MobClick event:eventId attributes:attributes];
}
+ (void)RS_event:(NSString *)eventId attributes:(NSDictionary *)attributes counter:(int)number{
[MobClick event:eventId attributes:attributes counter:number];
}
/*----------------------計算事件-----------------------*/
+ (void)RS_beginEvent:(NSString *)eventId{
[MobClick beginEvent:eventId];
}
+ (void)RS_endEvent:(NSString *)eventId{
[MobClick endEvent:eventId];
}
+ (void)RS_beginEvent:(NSString *)eventId label:(NSString *)label{
[MobClick beginEvent:eventId label:label];
}
+ (void)RS_endEvent:(NSString *)eventId label:(NSString *)label{
[MobClick endEvent:eventId label:label];
}
+ (void)RS_beginEvent:(NSString *)eventId primarykey :(NSString *)keyName attributes:(NSDictionary *)attributes{
[MobClick beginEvent:eventId primarykey:keyName attributes:attributes];
}
+ (void)RS_endEvent:(NSString *)eventId primarykey:(NSString *)keyName{
[MobClick endEvent:eventId primarykey:keyName];
}
+ (void)RS_event:(NSString *)eventId durations:(int)millisecond{
[MobClick event:eventId durations:millisecond];
}
+ (void)RS_event:(NSString *)eventId label:(NSString *)label durations:(int)millisecond{
[MobClick event:eventId label:label durations:millisecond];
}
+ (void)RS_event:(NSString *)eventId attributes:(NSDictionary *)attributes durations:(int)millisecond{
[MobClick event:eventId attributes:attributes durations:millisecond];
}
/*----------------------頁面統(tǒng)計-----------------------*/
+ (void)RS_logPageView:(NSString *)pageName seconds:(int)seconds{
[MobClick logPageView:pageName seconds:seconds];
}
+ (void)RS_beginLogPageView:(NSString *)pageName{
[MobClick beginLogPageView:pageName];
}
+ (void)RS_endLogPageView:(NSString *)pageName{
[MobClick endLogPageView:pageName];
}
#pragma mark - private
#pragma mark - 待添加交換方法
- (void)_RSAnalytics_viewWillAppear:(BOOL)animated{
NSString* pageName = [RSAppAnalyticsManager _RS_getViewControllerPageName:self];
if (pageName && pageName.length > 0) {
[RSAppAnalyticsManager RS_beginLogPageView:pageName];
}
[self _RSAnalytics_viewWillAppear:animated];
}
- (void)_RSAnalytics_viewWillDisappear:(BOOL)animated{
NSString* pageName = [RSAppAnalyticsManager _RS_getViewControllerPageName:self];
if (pageName && pageName.length > 0) {
[RSAppAnalyticsManager RS_endLogPageView:pageName];
}
[self _RSAnalytics_viewWillDisappear:animated];
}
+ (NSString*)_RS_getViewControllerPageName:(id)vcController{
UIViewController* controller = (UIViewController*)vcController;
NSString* pageName = [controller respondsToSelector:@selector(title)] ? controller.title : nil;
return pageName;
}
#pragma mark - 為子類控制器類添加viewWillAppear:和viewWillDisappear:確笔拭常控制器一定實現(xiàn)了這兩個方法,避免因未實現(xiàn)而交換UIViewController的方法實現(xiàn)涝桅,導(dǎo)致作用到全局
/**
為類添加viewWillAppear:和viewWillDisappear:方法
@param clazz 目標類
@param viewController 方法來源控制器(即參照控制器添加一個相同的方法到目標類中拜姿,如果viewController沒有則根據(jù)繼承關(guān)系逐層查找,類似copy一個方法到目標類冯遂,不會影響原類)
*/
+ (void)_RS_addViewWillAppearAndDisappearForSubClass:(Class)clazz formViewController:(UIViewController*)viewController{
SEL addWillAppearSel = @selector(viewWillAppear:);
class_addMethod(clazz, addWillAppearSel, method_getImplementation(class_getInstanceMethod([viewController class], addWillAppearSel)), "v@:B");
SEL addWillDisappearSel = @selector(viewWillDisappear:);
class_addMethod(clazz, addWillDisappearSel, method_getImplementation(class_getInstanceMethod([viewController class], addWillDisappearSel)), "v@:B");
}
@end
對所有ViewController添加頁面統(tǒng)計方法的輔助分類
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
為設(shè)置全局頁面計算統(tǒng)計提供支撐
*/
@interface UIViewController (Analytics)
@end
NS_ASSUME_NONNULL_END
#import "UIViewController+Analytics.h"
#import "RSAppAnalyticsManager.h"
@implementation UIViewController (Analytics)
- (void)_RSAnalytics_viewWillAppear:(BOOL)animated{
NSString* pageName = [UIViewController _RS_getViewControllerPageName:self];
if (pageName && pageName.length > 0) {
[RSAppAnalyticsManager RS_beginLogPageView:pageName];
}
[self _RSAnalytics_viewWillAppear:animated];
}
- (void)_RSAnalytics_viewWillDisappear:(BOOL)animated{
NSString* pageName = [UIViewController _RS_getViewControllerPageName:self];
if (pageName && pageName.length > 0) {
[RSAppAnalyticsManager RS_endLogPageView:pageName];
}
[self _RSAnalytics_viewWillDisappear:animated];
}
+ (NSString*)_RS_getViewControllerPageName:(id)vcController{
UIViewController* controller = (UIViewController*)vcController;
NSString* pageName = controller.title;
return pageName;
}
@end
以下是對UIButton蕊肥、UIView相關(guān)事件統(tǒng)計封裝
UIButton
#import <UIKit/UIKit.h>
#import "UIView+RSAnalytics.h"
/**
UIButton事件計數(shù)統(tǒng)計分類
*/
@interface UIButton (RSAnalytics)
/**
為UIButton添加事件,自動添加計數(shù)統(tǒng)計功能蛤肌。
@param target 目標(響應(yīng)者)
@param action 事件
@param controlEvents 事件類型
@param eventId 統(tǒng)計事件id
@param lable 統(tǒng)計事件label
*/
- (void)RS_addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents annlyticEventId:(NSString*)eventId annlyticLable:(NSString*)lable;
@end
#import "UIButton+RSAnalytics.h"
#import <objc/runtime.h>
#import "RSAppAnalyticsManager.h"
static NSString* const RS_eventId;
static NSString* const RS_lable;
static const char* kSUBCLASS_BUTTON_IVAR_NAME = "_rsAnalytics_Target_Button";
@implementation UIButton (RSAnalytics)
- (void)RS_addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents annlyticEventId:(NSString*)eventId annlyticLable:(NSString*)lable{
if (![target respondsToSelector:action])
return;
objc_setAssociatedObject(self, &RS_eventId, eventId, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &RS_lable, lable, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(target, kSUBCLASS_BUTTON_IVAR_NAME, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
NSString* selName = [NSStringFromSelector(action) rangeOfString:@":"].location == NSNotFound ? NSStringFromSelector(@selector(RS_analyticsEvent_)) : NSStringFromSelector(@selector(RS_analyticsEvent_:));
const char* types = [NSStringFromSelector(action) rangeOfString:@":"].location == NSNotFound ? "v@:" : "v@:@";
SEL sel = NSSelectorFromString(selName);
IMP imp = class_getMethodImplementation(self.class, sel);
if (class_addMethod([target class], sel, imp, types)) {
Method actionMethod = class_getInstanceMethod([target class], action);
Method newMethod = class_getInstanceMethod([target class], sel);
method_exchangeImplementations(actionMethod, newMethod);
}
[self addTarget:target action:action forControlEvents:controlEvents];
}
- (void)RS_analyticsEvent_:(UIButton*)eventView{
[RSAppAnalyticsManager RS_event:eventView.eventId label:eventView.lable];
[self RS_analyticsEvent_:eventView];
}
- (void)RS_analyticsEvent_{
UIButton* eventView = objc_getAssociatedObject(self, kSUBCLASS_BUTTON_IVAR_NAME);
[RSAppAnalyticsManager RS_event:eventView.eventId label:eventView.lable];
[self RS_analyticsEvent_];
}
#pragma mark - getter
- (NSString *)eventId{
return objc_getAssociatedObject(self, &RS_eventId);
}
- (NSString *)lable{
return objc_getAssociatedObject(self, &RS_lable);
}
@end
UIView點擊事件
#import <UIKit/UIKit.h>
/**
視圖添加點擊事件分類(同時添加事件計數(shù)統(tǒng)計)
*/
@interface UIView (RSAnalytics)
/**
為視圖添加點擊事件;自動添加事件計數(shù)統(tǒng)計功能
@param target 目標(響應(yīng)者)
@param action 事件
@param eventId 統(tǒng)計事件id
@param lable 統(tǒng)計事件label
*/
- (void)RS_addTarget:(id)target forTouchUpInsaidAction:(SEL)action annlyticEventId:(NSString*)eventId annlyticLable:(NSString*)lable;
@end
#import "UIView+RSAnalytics.h"
#import <objc/message.h>
#import "RSAppAnalyticsManager.h"
static NSString* const RS_eventId;
static NSString* const RS_lable;
static const char* kSUBCLASS_VIEW_IVAR_NAME = "_rsAnalytics_Target_View";
@implementation UIView (RSAnalytics)
- (void)RS_addTarget:(id)target forTouchUpInsaidAction:(SEL)action annlyticEventId:(NSString*)eventId annlyticLable:(NSString*)lable{
if (![target respondsToSelector:action])
return;
if (!self.userInteractionEnabled)
self.userInteractionEnabled = YES;
objc_setAssociatedObject(self, &RS_eventId, eventId, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &RS_lable, lable, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(target, kSUBCLASS_VIEW_IVAR_NAME, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
NSString* selName = [NSStringFromSelector(action) rangeOfString:@":"].location == NSNotFound ? NSStringFromSelector(@selector(RS_analyticsEvent_)) : NSStringFromSelector(@selector(RS_analyticsEvent_:));
const char* types = [NSStringFromSelector(action) rangeOfString:@":"].location == NSNotFound ? "v@:" : "v@:@";
SEL sel = NSSelectorFromString(selName);
IMP imp = class_getMethodImplementation(self.class, sel);
if (class_addMethod([target class], sel, imp, types)) {
Method actionMethod = class_getInstanceMethod([target class], action);
Method newMethod = class_getInstanceMethod([target class], sel);
method_exchangeImplementations(actionMethod, newMethod);
}
UITapGestureRecognizer* tapGest = [[UITapGestureRecognizer alloc] initWithTarget:target action:action];
[self addGestureRecognizer:tapGest];
}
- (void)RS_analyticsEvent_:(UIGestureRecognizer*)ges{
[RSAppAnalyticsManager RS_event:ges.view.eventId label:ges.view.lable];
[self RS_analyticsEvent_:ges];
}
- (void)RS_analyticsEvent_{
UIButton* eventView = objc_getAssociatedObject(self, kSUBCLASS_VIEW_IVAR_NAME);
[RSAppAnalyticsManager RS_event:eventView.eventId label:eventView.lable];
[self RS_analyticsEvent_];
}
#pragma mark - getter setter
- (NSString *)eventId{
return objc_getAssociatedObject(self, &RS_eventId);
}
- (NSString *)lable{
return objc_getAssociatedObject(self, &RS_lable);
}
@end
初版壁却,沒有深入測試過,如有問題或者更好的實現(xiàn)方法可在評論中留言