2025-12-25 17:20:24 +08:00
|
|
|
|
//
|
|
|
|
|
|
// KBMaiPointReporter.m
|
|
|
|
|
|
// keyBoard
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#import "KBMaiPointReporter.h"
|
2025-12-26 15:51:27 +08:00
|
|
|
|
#import "KBLog.h"
|
2026-01-06 19:25:34 +08:00
|
|
|
|
#import "KBAuthManager.h"
|
|
|
|
|
|
#if __has_include(<UIKit/UIKit.h>)
|
|
|
|
|
|
#import <UIKit/UIKit.h>
|
|
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
|
|
#endif
|
2025-12-25 17:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
NSString * const KBMaiPointErrorDomain = @"KBMaiPointErrorDomain";
|
2026-01-06 19:25:34 +08:00
|
|
|
|
NSString * const KBMaiPointEventTypePageExposure = @"page_exposure";
|
|
|
|
|
|
NSString * const KBMaiPointEventTypeClick = @"click";
|
2025-12-25 17:20:24 +08:00
|
|
|
|
|
2025-12-26 15:51:27 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
static void KBMaiPoint_DebugLogURL(NSURLRequest *request) {
|
|
|
|
|
|
NSString *url = request.URL.absoluteString ?: @"";
|
|
|
|
|
|
KBLOG(@"🍃[KBMaiPointReporter] url=%@", url);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) {
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
NSString *msg = error.localizedDescription ?: @"(no description)";
|
|
|
|
|
|
KBLOG(@"🍃[KBMaiPointReporter] error=%@ domain=%@ code=%ld", msg, error.domain ?: @"", (long)error.code);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ([response isKindOfClass:NSHTTPURLResponse.class]) {
|
|
|
|
|
|
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
|
|
|
|
|
if (statusCode >= 200 && statusCode < 300) {
|
|
|
|
|
|
KBLOG(@"🍃[KBMaiPointReporter] status=HTTP_%ld", (long)statusCode);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
KBLOG(@"🍃[KBMaiPointReporter] error=HTTP_%ld", (long)statusCode);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-12-25 17:20:24 +08:00
|
|
|
|
@implementation KBMaiPointReporter
|
|
|
|
|
|
|
|
|
|
|
|
+ (instancetype)sharedReporter {
|
|
|
|
|
|
static KBMaiPointReporter *reporter = nil;
|
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
|
reporter = [[KBMaiPointReporter alloc] init];
|
|
|
|
|
|
});
|
|
|
|
|
|
return reporter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 15:27:35 +08:00
|
|
|
|
- (NSString *)kb_trimmedStringOrEmpty:(NSString * _Nullable)string {
|
|
|
|
|
|
NSString *value = [string isKindOfClass:[NSString class]] ? string : @"";
|
|
|
|
|
|
return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] ?: @"";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 19:25:34 +08:00
|
|
|
|
- (NSString *)kb_currentTokenOrEmpty {
|
|
|
|
|
|
NSString *t = [KBAuthManager shared].current.accessToken;
|
|
|
|
|
|
return [self kb_trimmedStringOrEmpty:t];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)reportEventType:(NSString *)eventType
|
|
|
|
|
|
eventName:(NSString *)eventName
|
|
|
|
|
|
value:(NSDictionary * _Nullable)value
|
|
|
|
|
|
completion:(KBMaiPointReportCompletion _Nullable)completion {
|
|
|
|
|
|
NSString *trimmedType = [self kb_trimmedStringOrEmpty:eventType];
|
|
|
|
|
|
NSString *trimmedName = [self kb_trimmedStringOrEmpty:eventName];
|
|
|
|
|
|
if (trimmedType.length == 0 || trimmedName.length == 0) {
|
|
|
|
|
|
NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain
|
|
|
|
|
|
code:-1
|
|
|
|
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid parameter"}];
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
completion(NO, error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSMutableDictionary *val = [NSMutableDictionary dictionary];
|
|
|
|
|
|
if ([value isKindOfClass:[NSDictionary class]] && value.count > 0) {
|
|
|
|
|
|
[val addEntriesFromDictionary:value];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (![val[@"token"] isKindOfClass:NSString.class]) {
|
|
|
|
|
|
val[@"token"] = [self kb_currentTokenOrEmpty];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 若外部传了 token,也做一次兜底(nil -> @"" / trim)
|
|
|
|
|
|
val[@"token"] = [self kb_trimmedStringOrEmpty:val[@"token"]];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSDictionary *params = @{
|
|
|
|
|
|
// 字段兼容:后端若用 eventId 统计,也能直接用 eventName
|
|
|
|
|
|
@"eventType": trimmedType,
|
|
|
|
|
|
@"eventName": trimmedName,
|
|
|
|
|
|
@"eventId": trimmedName,
|
|
|
|
|
|
@"value": val.copy
|
|
|
|
|
|
};
|
|
|
|
|
|
[self postPath:KB_MAI_POINT_PATH_GENERIC_DATA parameters:params completion:completion];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)reportPageExposureWithEventName:(NSString *)eventName
|
|
|
|
|
|
pageId:(NSString *)pageId
|
|
|
|
|
|
extra:(NSDictionary * _Nullable)extra
|
|
|
|
|
|
completion:(KBMaiPointReportCompletion _Nullable)completion {
|
|
|
|
|
|
NSString *pid = [self kb_trimmedStringOrEmpty:pageId];
|
|
|
|
|
|
NSMutableDictionary *val = [NSMutableDictionary dictionary];
|
|
|
|
|
|
if (pid.length > 0) {
|
|
|
|
|
|
val[@"page_id"] = pid;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ([extra isKindOfClass:[NSDictionary class]] && extra.count > 0) {
|
|
|
|
|
|
[val addEntriesFromDictionary:extra];
|
|
|
|
|
|
}
|
|
|
|
|
|
[self reportEventType:KBMaiPointEventTypePageExposure eventName:eventName value:val completion:completion];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)reportClickWithEventName:(NSString *)eventName
|
|
|
|
|
|
pageId:(NSString *)pageId
|
|
|
|
|
|
elementId:(NSString *)elementId
|
|
|
|
|
|
extra:(NSDictionary * _Nullable)extra
|
|
|
|
|
|
completion:(KBMaiPointReportCompletion _Nullable)completion {
|
|
|
|
|
|
NSString *pid = [self kb_trimmedStringOrEmpty:pageId];
|
|
|
|
|
|
NSString *eid = [self kb_trimmedStringOrEmpty:elementId];
|
|
|
|
|
|
NSMutableDictionary *val = [NSMutableDictionary dictionary];
|
|
|
|
|
|
if (pid.length > 0) {
|
|
|
|
|
|
val[@"page_id"] = pid;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (eid.length > 0) {
|
|
|
|
|
|
val[@"element_id"] = eid;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ([extra isKindOfClass:[NSDictionary class]] && extra.count > 0) {
|
|
|
|
|
|
[val addEntriesFromDictionary:extra];
|
|
|
|
|
|
}
|
|
|
|
|
|
[self reportEventType:KBMaiPointEventTypeClick eventName:eventName value:val completion:completion];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-25 17:20:24 +08:00
|
|
|
|
- (void)reportNewAccountWithType:(NSString *)type
|
2025-12-30 15:27:35 +08:00
|
|
|
|
account:(NSString * _Nullable)account
|
|
|
|
|
|
completion:(KBMaiPointReportCompletion _Nullable)completion {
|
|
|
|
|
|
NSString *trimmedType = [self kb_trimmedStringOrEmpty:type];
|
|
|
|
|
|
NSString *trimmedAccount = [self kb_trimmedStringOrEmpty:account];
|
2026-01-06 19:25:34 +08:00
|
|
|
|
if (trimmedType.length == 0) {
|
2025-12-25 17:20:24 +08:00
|
|
|
|
NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain
|
|
|
|
|
|
code:-1
|
|
|
|
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid parameter"}];
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
completion(NO, error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSDictionary *params = @{
|
|
|
|
|
|
@"type": trimmedType,
|
2026-01-06 19:25:34 +08:00
|
|
|
|
@"account": trimmedAccount ?: @"",
|
|
|
|
|
|
@"token": [self kb_currentTokenOrEmpty]
|
2025-12-25 17:20:24 +08:00
|
|
|
|
};
|
|
|
|
|
|
[self postPath:KB_MAI_POINT_PATH_NEW_ACCOUNT parameters:params completion:completion];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 15:27:35 +08:00
|
|
|
|
//- (void)reportGenericDataWithEvent:(NSString *)event
|
|
|
|
|
|
// account:(NSString * _Nullable)account
|
|
|
|
|
|
// completion:(KBMaiPointReportCompletion _Nullable)completion {
|
|
|
|
|
|
// [self reportGenericDataWithType:KBMaiPointGenericReportTypeUnknown
|
|
|
|
|
|
// event:event
|
|
|
|
|
|
// account:account
|
|
|
|
|
|
// completion:completion];
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)reportGenericDataWithEventType:(KBMaiPointGenericReportType)eventType
|
|
|
|
|
|
account:(nullable NSString *)account
|
|
|
|
|
|
completion:(KBMaiPointReportCompletion _Nullable)completion{
|
2026-01-06 19:25:34 +08:00
|
|
|
|
// 兼容旧接口:没有 eventName 时给一个默认值,避免调用方崩溃
|
|
|
|
|
|
NSString *typeStr = @"unknown";
|
|
|
|
|
|
switch (eventType) {
|
|
|
|
|
|
case KBMaiPointGenericReportTypeClick: typeStr = KBMaiPointEventTypeClick; break;
|
|
|
|
|
|
case KBMaiPointGenericReportTypeExposure: typeStr = @"exposure"; break;
|
|
|
|
|
|
case KBMaiPointGenericReportTypePage: typeStr = KBMaiPointEventTypePageExposure; break;
|
|
|
|
|
|
default: break;
|
|
|
|
|
|
}
|
|
|
|
|
|
NSMutableDictionary *val = [NSMutableDictionary dictionary];
|
2025-12-30 15:27:35 +08:00
|
|
|
|
NSString *trimmedAccount = [self kb_trimmedStringOrEmpty:account];
|
2026-01-06 19:25:34 +08:00
|
|
|
|
if (trimmedAccount.length > 0) {
|
|
|
|
|
|
val[@"account"] = trimmedAccount;
|
2025-12-30 15:27:35 +08:00
|
|
|
|
}
|
2026-01-06 19:25:34 +08:00
|
|
|
|
[self reportEventType:typeStr eventName:@"generic_event" value:val completion:completion];
|
2025-12-30 15:27:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-25 17:20:24 +08:00
|
|
|
|
- (void)postPath:(NSString *)path
|
|
|
|
|
|
parameters:(NSDictionary *)parameters
|
2025-12-30 15:27:35 +08:00
|
|
|
|
completion:(KBMaiPointReportCompletion _Nullable)completion {
|
2025-12-25 17:20:24 +08:00
|
|
|
|
if (path.length == 0 || ![parameters isKindOfClass:[NSDictionary class]]) {
|
|
|
|
|
|
NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain
|
|
|
|
|
|
code:-1
|
|
|
|
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid parameter"}];
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
completion(NO, error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSString *safePath = [path hasPrefix:@"/"] ? path : [@"/" stringByAppendingString:path];
|
|
|
|
|
|
NSString *urlString = [NSString stringWithFormat:@"%@%@", KB_MAI_POINT_BASE_URL, safePath];
|
|
|
|
|
|
NSURL *url = [NSURL URLWithString:urlString];
|
|
|
|
|
|
if (!url) {
|
|
|
|
|
|
NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain
|
|
|
|
|
|
code:-2
|
|
|
|
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid URL"}];
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
completion(NO, error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSError *jsonError = nil;
|
|
|
|
|
|
NSData *body = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&jsonError];
|
|
|
|
|
|
if (jsonError) {
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
completion(NO, jsonError);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
|
|
|
|
|
request.HTTPMethod = @"POST";
|
|
|
|
|
|
request.timeoutInterval = 10.0;
|
|
|
|
|
|
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
|
|
|
|
|
request.HTTPBody = body;
|
|
|
|
|
|
|
2025-12-26 15:51:27 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
KBMaiPoint_DebugLogURL(request);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-12-25 17:20:24 +08:00
|
|
|
|
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
|
|
|
|
|
config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
|
|
|
|
|
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
|
|
|
|
|
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
|
|
|
|
|
|
completionHandler:^(NSData *data,
|
|
|
|
|
|
NSURLResponse *response,
|
|
|
|
|
|
NSError *error) {
|
|
|
|
|
|
BOOL success = NO;
|
|
|
|
|
|
NSError *finalError = error;
|
|
|
|
|
|
|
|
|
|
|
|
if (!finalError) {
|
|
|
|
|
|
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
|
|
|
|
|
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
|
|
|
|
|
success = (statusCode >= 200 && statusCode < 300);
|
|
|
|
|
|
if (!success) {
|
|
|
|
|
|
finalError = [NSError errorWithDomain:KBMaiPointErrorDomain
|
|
|
|
|
|
code:statusCode
|
|
|
|
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid response"}];
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
finalError = [NSError errorWithDomain:KBMaiPointErrorDomain
|
|
|
|
|
|
code:-3
|
|
|
|
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid response"}];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 15:51:27 +08:00
|
|
|
|
#if DEBUG
|
|
|
|
|
|
KBMaiPoint_DebugLogError(response, finalError);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-12-25 17:20:24 +08:00
|
|
|
|
if (completion) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
completion(success, finalError);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}];
|
|
|
|
|
|
[task resume];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@end
|
2026-01-06 19:25:34 +08:00
|
|
|
|
|
|
|
|
|
|
#if __has_include(<UIKit/UIKit.h>)
|
|
|
|
|
|
|
|
|
|
|
|
// ============================
|
|
|
|
|
|
// 自动页面曝光(viewDidAppear)
|
|
|
|
|
|
// 说明:仅对“在表里登记过的 VC”生效;其它 VC 不处理。
|
|
|
|
|
|
// ============================
|
|
|
|
|
|
|
|
|
|
|
|
static NSDictionary<NSString *, NSDictionary *> *KBMaiPoint_PageExposureMap(void) {
|
|
|
|
|
|
static NSDictionary<NSString *, NSDictionary *> *m;
|
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
|
m = @{
|
|
|
|
|
|
// 主工程
|
|
|
|
|
|
@"HomeMainVC": @{@"event_name": @"enter_home_main", @"page_id": @"home_main"},
|
|
|
|
|
|
@"HomeVC": @{@"event_name": @"enter_home", @"page_id": @"home"},
|
|
|
|
|
|
@"HomeHotVC": @{@"event_name": @"enter_home_hot", @"page_id": @"home_hot"},
|
|
|
|
|
|
@"HomeRankVC": @{@"event_name": @"enter_home_rank", @"page_id": @"home_rank"},
|
|
|
|
|
|
@"HomeRankContentVC": @{@"event_name": @"enter_home_rank_content", @"page_id": @"home_rank_content"},
|
|
|
|
|
|
@"HomeSheetVC": @{@"event_name": @"enter_home_sheet", @"page_id": @"home_sheet"},
|
|
|
|
|
|
@"KBCommunityVC": @{@"event_name": @"enter_community", @"page_id": @"community"},
|
|
|
|
|
|
@"KBSearchVC": @{@"event_name": @"enter_search", @"page_id": @"search"},
|
|
|
|
|
|
@"KBSearchResultVC": @{@"event_name": @"enter_search_result", @"page_id": @"search_result"},
|
|
|
|
|
|
@"KBShopVC": @{@"event_name": @"enter_shop", @"page_id": @"shop"},
|
|
|
|
|
|
@"KBShopItemVC": @{@"event_name": @"enter_shop_item_list", @"page_id": @"shop_item_list"},
|
|
|
|
|
|
@"KBSkinDetailVC": @{@"event_name": @"enter_skin_detail", @"page_id": @"skin_detail"},
|
|
|
|
|
|
@"MyVC": @{@"event_name": @"enter_my", @"page_id": @"my"},
|
|
|
|
|
|
@"MySkinVC": @{@"event_name": @"enter_my_skin", @"page_id": @"my_skin"},
|
|
|
|
|
|
@"KBMyKeyBoardVC": @{@"event_name": @"enter_my_keyboard", @"page_id": @"my_keyboard"},
|
|
|
|
|
|
@"KBPersonInfoVC": @{@"event_name": @"enter_person_info", @"page_id": @"person_info"},
|
|
|
|
|
|
@"KBFeedBackVC": @{@"event_name": @"enter_feedback", @"page_id": @"feedback"},
|
|
|
|
|
|
@"KBNoticeVC": @{@"event_name": @"enter_notice", @"page_id": @"notice"},
|
|
|
|
|
|
@"KBConsumptionRecordVC": @{@"event_name": @"enter_consumption_record", @"page_id": @"consumption_record"},
|
|
|
|
|
|
@"KBVipPay": @{@"event_name": @"enter_vip_pay", @"page_id": @"vip_pay"},
|
|
|
|
|
|
@"KBJfPay": @{@"event_name": @"enter_points_recharge", @"page_id": @"points_recharge"},
|
|
|
|
|
|
@"KBLoginVC": @{@"event_name": @"enter_login", @"page_id": @"login"},
|
|
|
|
|
|
@"KBEmailLoginVC": @{@"event_name": @"enter_login_email", @"page_id": @"login_email"},
|
|
|
|
|
|
@"KBEmailRegistVC": @{@"event_name": @"enter_register_email", @"page_id": @"register_email"},
|
|
|
|
|
|
@"KBRegistVerEmailVC": @{@"event_name": @"enter_register_verify_email", @"page_id": @"register_verify_email"},
|
|
|
|
|
|
@"KBForgetPwdVC": @{@"event_name": @"enter_forgot_password_email", @"page_id": @"forgot_password_email"},
|
|
|
|
|
|
@"KBForgetVerPwdVC": @{@"event_name": @"enter_forgot_password_verify", @"page_id": @"forgot_password_verify"},
|
|
|
|
|
|
@"KBForgetPwdNewPwdVC": @{@"event_name": @"enter_forgot_password_newpwd", @"page_id": @"forgot_password_newpwd"},
|
|
|
|
|
|
@"KBPermissionViewController": @{@"event_name": @"enter_keyboard_permission_guide", @"page_id": @"keyboard_permission_guide"},
|
|
|
|
|
|
@"KBGuideVC": @{@"event_name": @"enter_guide", @"page_id": @"guide"},
|
|
|
|
|
|
@"KBSexSelVC": @{@"event_name": @"enter_sex_select", @"page_id": @"sex_select"},
|
|
|
|
|
|
@"KBWebViewViewController": @{@"event_name": @"enter_webview", @"page_id": @"webview"},
|
|
|
|
|
|
|
|
|
|
|
|
// 键盘扩展
|
|
|
|
|
|
@"KeyboardViewController": @{@"event_name": @"enter_keyboard", @"page_id": @"keyboard"},
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
return m;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline void KBMaiPoint_SwizzleInstanceMethod(Class cls, SEL originalSel, SEL swizzledSel) {
|
|
|
|
|
|
Method original = class_getInstanceMethod(cls, originalSel);
|
|
|
|
|
|
Method swizzled = class_getInstanceMethod(cls, swizzledSel);
|
|
|
|
|
|
if (!original || !swizzled) return;
|
|
|
|
|
|
BOOL added = class_addMethod(cls,
|
|
|
|
|
|
originalSel,
|
|
|
|
|
|
method_getImplementation(swizzled),
|
|
|
|
|
|
method_getTypeEncoding(swizzled));
|
|
|
|
|
|
if (added) {
|
|
|
|
|
|
class_replaceMethod(cls,
|
|
|
|
|
|
swizzledSel,
|
|
|
|
|
|
method_getImplementation(original),
|
|
|
|
|
|
method_getTypeEncoding(original));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
method_exchangeImplementations(original, swizzled);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@interface UIViewController (KBMaiPointAutoReport)
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation UIViewController (KBMaiPointAutoReport)
|
|
|
|
|
|
|
|
|
|
|
|
+ (void)load {
|
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
|
KBMaiPoint_SwizzleInstanceMethod(self, @selector(viewDidAppear:), @selector(kb_maipoint_viewDidAppear:));
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)kb_maipoint_viewDidAppear:(BOOL)animated {
|
|
|
|
|
|
[self kb_maipoint_viewDidAppear:animated];
|
|
|
|
|
|
|
|
|
|
|
|
NSString *clsName = NSStringFromClass(self.class);
|
|
|
|
|
|
NSDictionary *cfg = KBMaiPoint_PageExposureMap()[clsName];
|
|
|
|
|
|
if (![cfg isKindOfClass:NSDictionary.class]) { return; }
|
|
|
|
|
|
|
|
|
|
|
|
NSString *eventName = cfg[@"event_name"];
|
|
|
|
|
|
NSString *pageId = cfg[@"page_id"];
|
|
|
|
|
|
if (![eventName isKindOfClass:NSString.class] || ![pageId isKindOfClass:NSString.class]) { return; }
|
|
|
|
|
|
|
|
|
|
|
|
// 少数页面带额外参数(尽量不取敏感信息)
|
|
|
|
|
|
NSMutableDictionary *extra = [NSMutableDictionary dictionary];
|
|
|
|
|
|
if ([clsName isEqualToString:@"KBSkinDetailVC"]) {
|
|
|
|
|
|
id themeId = nil;
|
|
|
|
|
|
@try { themeId = [self valueForKey:@"themeId"]; } @catch (__unused NSException *e) { themeId = nil; }
|
|
|
|
|
|
if ([themeId isKindOfClass:NSString.class] && ((NSString *)themeId).length > 0) {
|
|
|
|
|
|
extra[@"theme_id"] = themeId;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if ([clsName isEqualToString:@"KBWebViewViewController"]) {
|
|
|
|
|
|
id url = nil;
|
|
|
|
|
|
@try { url = [self valueForKey:@"url"]; } @catch (__unused NSException *e) { url = nil; }
|
|
|
|
|
|
if ([url isKindOfClass:NSString.class] && ((NSString *)url).length > 0) {
|
|
|
|
|
|
extra[@"url"] = url;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[[KBMaiPointReporter sharedReporter] reportPageExposureWithEventName:eventName
|
|
|
|
|
|
pageId:pageId
|
|
|
|
|
|
extra:(extra.count > 0 ? extra.copy : nil)
|
|
|
|
|
|
completion:nil];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|