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的健壮性。