aaaaaa# 模拟神策(Sensors Analytics)SDK 核心实现逻辑
为了模拟神策 SDK 的无侵入埋点采集功能,我们将创建一个简化的框架,涵盖自动追踪事件、事件数据管理以及网络请求管理等关键方面。以下是如何构建这样一个简化版的 SDK。
1. SensorsAnalyticsManager 类定义
1.1 SensorsAnalyticsManager.h
aaaa```objc
// SensorsAnalyticsManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol SensorsAnalyticsDelegate
- (void)didCollectEventData:(NSDictionary *)eventData;
@end
@interface SensorsAnalyticsManager : NSObject
@property (nonatomic, weak) id delegate;
- (instancetype)sharedInstance;
-
(void)startWithAppKey:(NSString *)appKey;
-
(void)trackEvent:(NSString *)eventName withProperties:(nullable NSDictionary<NSString *,id> *)properties;
-
(void)autoTrackScreenView:(UIViewController *)viewController;
-
(void)autoTrackControlAction:(UIControl *)control;
-
(void)flushEventData;
-
(void)sendEventDataToServer;
@end
NS_ASSUME_NONNULL_END
aaaa
### 2. 实现逻辑
#### 2.1 自动追踪页面浏览
通过 AOP 或者运行时方法交换来捕获屏幕视图的变化,并记录页面停留时间。
#### 2.2 自动追踪控件动作
类似地,对按钮点击等 UI 控制的动作进行拦截和追踪。
#### 2.3 本地存储与上报
使用 `NSUserDefaults` 或者 SQLite 等方式持久化事件数据,在适当的时机批量发送到服务器。
### 3. 使用说明
在应用程序启动时初始化 `SensorsAnalyticsManager` 并设置必要的参数如 appKey,然后可以开始自动或手动跟踪用户行为。
此示例提供了一个基本框架,具体实现可能需要根据实际需求进一步开发和完善。
```objc
#import <Foundation/Foundation.h>
@interface SensorsAnalyticsManager : NSObject
+ (instancetype)sharedInstance;
// 开始 SDK 监控
- (void)startTracking;
// 上报事件
- (void)trackEvent:(NSString *)eventName withProperties:(NSDictionary *)properties;
@end
1.2 SensorsAnalyticsManager.m
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
|
#import "SensorsAnalyticsManager.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
@implementation SensorsAnalyticsManager
+ (instancetype)sharedInstance {
static SensorsAnalyticsManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SensorsAnalyticsManager alloc] init];
});
return sharedInstance;
}
- (void)startTracking {
// 启动页面和点击事件的埋点追踪
[self swizzleUIViewControllerLifecycle];
[self swizzleUIControlEventTracking];
}
#pragma mark - 页面停留时长追踪
- (void)swizzleUIViewControllerLifecycle {
Class class = [UIViewController class];
SEL originalAppearSelector = @selector(viewDidAppear:);
SEL swizzledAppearSelector = @selector(sa_viewDidAppear:);
[self swizzleMethodInClass:class originalSelector:originalAppearSelector swizzledSelector:swizzledAppearSelector];
SEL originalDisappearSelector = @selector(viewWillDisappear:);
SEL swizzledDisappearSelector = @selector(sa_viewWillDisappear:);
[self swizzleMethodInClass:class originalSelector:originalDisappearSelector swizzledSelector:swizzledDisappearSelector];
}
- (void)swizzleMethodInClass:(Class)class originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
@end
|
1.3 UIViewController 类别实现页面埋点逻辑
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
|
#import "SensorsAnalyticsManager.h"
#import <objc/runtime.h>
@implementation UIViewController (SensorsAnalytics)
- (void)sa_viewDidAppear:(BOOL)animated {
[self sa_viewDidAppear:animated]; // 调用原始的 viewDidAppear
// 记录进入页面的时间
NSString *pageName = NSStringFromClass([self class]);
NSLog(@"Enter page: %@", pageName);
objc_setAssociatedObject(self, @selector(sa_viewDidAppear:), [NSDate date], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 发送页面浏览事件
[[SensorsAnalyticsManager sharedInstance] trackEvent:@"PageView"
withProperties:@{@"pageName": pageName}];
}
- (void)sa_viewWillDisappear:(BOOL)animated {
[self sa_viewWillDisappear:animated]; // 调用原始的 viewWillDisappear
// 计算页面停留时长
NSDate *enterTime = objc_getAssociatedObject(self, @selector(sa_viewDidAppear:));
if (enterTime) {
NSTimeInterval stayDuration = [[NSDate date] timeIntervalSinceDate:enterTime];
NSString *pageName = NSStringFromClass([self class]);
NSLog(@"Exit page: %@, Stay Duration: %.2f seconds", pageName, stayDuration);
// 发送页面停留时长事件
[[SensorsAnalyticsManager sharedInstance] trackEvent:@"PageStay"
withProperties:@{@"pageName": pageName, @"duration": @(stayDuration)}];
}
}
@end
|
1.4 追踪 UIControl 点击事件
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
|
#import <objc/runtime.h>
@implementation UIControl (SensorsAnalytics)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleControlEvents];
});
}
+ (void)swizzleControlEvents {
SEL originalSelector = @selector(sendAction:to:forEvent:);
SEL swizzledSelector = @selector(sa_sendAction:to:forEvent:);
Method originalMethod = class_getInstanceMethod([self class], originalSelector);
Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
- (void)sa_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
[self sa_sendAction:action to:target forEvent:event]; // 调用原始方法
// 发送点击事件埋点
NSString *actionName = NSStringFromSelector(action);
NSString *controlName = NSStringFromClass([self class]);
[[SensorsAnalyticsManager sharedInstance] trackEvent:@"UIControlClick"
withProperties:@{@"control": controlName, @"action": actionName}];
}
@end
|
1.5 事件数据的上报和存储
神策的 SDK 会先将事件存储在本地,等积累到一定量或者满足某些条件时再统一上报服务器。
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
|
@implementation SensorsAnalyticsManager
// 埋点事件追踪方法
- (void)trackEvent:(NSString *)eventName withProperties:(NSDictionary *)properties {
NSMutableDictionary *eventData = [NSMutableDictionary dictionary];
eventData[@"eventName"] = eventName;
eventData[@"properties"] = properties;
eventData[@"timestamp"] = @([[NSDate date] timeIntervalSince1970]);
// 模拟将事件存储到本地
[self saveEvent:eventData];
// 如果达到一定数量,或满足条件,触发上报逻辑
if ([self shouldUploadEvents]) {
[self uploadEvents];
}
}
- (void)saveEvent:(NSDictionary *)eventData {
// 模拟本地存储,可以使用文件、数据库等方式持久化
NSLog(@"Saved event: %@", eventData);
}
- (BOOL)shouldUploadEvents {
// 模拟条件,比如超过 10 条事件,或者达到时间阈值
return YES;
}
- (void)uploadEvents {
// 模拟事件上报,可以使用 NSURLSession 或其他网络库
NSLog(@"Uploading events...");
}
@end
|
这个实现模拟了神策埋点SDK的核心逻辑,通过Runtime方法交换实现无侵入的页面停留时长采集和UI事件追踪。埋点数据会本地存储,并根据条件统一上报服务器。在实际生产环境中,还需要处理更多的边界情况,如网络失败重试、数据加密、日志管理等,确保SDK的健壮性。