// ============================================== // U3DAnalytics 1.12.0 // Native Framework Version 0.3.6 // update date: 2024-08-06 -- by HuYufei // // Created by HuYufei on 2022/11/17. // Copyright © 2022 guru. All rights reserved. // ============================================== #import #import "UnityAppController+UnityInterface.h" #import @interface U3DAnalytics : NSObject //+ (void)requestGDPR: (NSString *)deviceId :(int) debugGeo; //+ (UIViewController *) getUnityViewController; @end static NSString *gameObjectName = @"GuruCallback"; static NSString *callbackName =@"OnCallback"; static GuruAnalytics *_analytics; @implementation U3DAnalytics // Const value define NSString * const Version = @"1.12.0"; static const double kUploadPeriodInSecond = 60.0; static const int kBatchLimit = 15; static const double kEventExpiredSeconds = 7 * 24 * 60 * 60; static const double kInitializeTimeout = 5.0; static double tch001MaxValue = 0.01; static double tch02MaxValue = 0.2; static bool enableErrorLog = false; static int eventCountAll; static int eventCountUploaded; NSString * const TchAdRevRoas001 = @"tch_ad_rev_roas_001"; NSString * const TchAdRevRoas02 = @"tch_ad_rev_roas_02"; NSString * const TchError = @"tch_error"; +(UnityAppController *)GetAppController { return (UnityAppController*)[UIApplication sharedApplication].delegate; } +(UIViewController *) getUnityViewController { return UnityGetGLViewController(); } // 字符串转换 +(const char*) stringToChar: (NSString *) str{ return [str cStringUsingEncoding:NSASCIIStringEncoding]; } +(NSString *) charToString: (const char *) c{ return [[NSString alloc] initWithUTF8String:c]; } // 获取最终字符串 +(char*) finalChar: (NSString *) string { const char *tmpChar = [string cStringUsingEncoding:NSASCIIStringEncoding]; if (string == NULL) return NULL; char* res = (char*)malloc(strlen(tmpChar) + 1); strcpy(res, tmpChar); return res; } // 设置太极02的预设值 +(void) setTch02MaxValue: (const double) value{ tch02MaxValue = value; } // 设置是否启用日志错误上报 +(void) setEnableErrorLog: (bool) value{ if(enableErrorLog == false && value){ enableErrorLog = value; [GuruAnalytics registerInternalEventObserverWithReportCallback:^(NSInteger code, NSString * info){ [U3DAnalytics onEventCallback:code andInfo:info]; }]; } } // 事件上报回调 +(void) getEventsStatistics { [GuruAnalytics debug_eventsStatistics:^(NSInteger uploadedEventsCount, NSInteger loggedEventsCount) { // 上报事件总量 eventCountAll = (int)uploadedEventsCount; // 上报成功数量 eventCountUploaded = (int)loggedEventsCount; }]; } // 设置 BaseUrl +(void) setBaseUrl: (const char *) baseUrl{ if (baseUrl != nullptr && strlen(baseUrl) == 0) { return; // baseUrl 为空 } [GuruAnalytics setEventsUploadEndPointWithHost:[U3DAnalytics charToString:baseUrl]]; } // 事件上报回调 +(void) onEventCallback: (NSInteger)code andInfo:(NSString *) info{ [U3DAnalytics sendMessage: [U3DAnalytics buildLogEventString: code andMessage:info]]; } // 构建数据 +(NSString *) buildLogEventString: (NSInteger)status andMessage: (NSString *)msg{ NSString *jsonString = [NSString stringWithFormat: @"{\"action\":\"logger_error\",\"data\":{\"code\":%d,\"msg\":\"%@\"}}", (int)status, msg]; return jsonString; } // 构建数据字典 +(NSDictionary *) buildDataDict: (NSString *) str{ NSArray *raw = [str componentsSeparatedByString:@","]; if( raw == nil || raw.count == 0) return nil; NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; //must init before using for(NSString *s in raw ){ NSArray *kvp = [s componentsSeparatedByString:@":"]; if(kvp == nil || kvp.count < 2){ continue; } NSString *k = kvp[0]; NSString *t = [kvp[1] substringToIndex:1]; NSString *v = [kvp[1] substringFromIndex:1]; // NSLog(@"---[iOS] parse kvp key:%@ type:%@ value:%@", k, t, v); //TODO 解析字符值 if([t isEqual: @"i"]){ // int [dict setValue:@([v integerValue]) forKey:k]; }else if([t isEqual: @"d"]){ // double [dict setValue:@([v doubleValue]) forKey:k]; } else { // String [dict setValue:v forKey:k]; } } return dict; } // 构建Json格式的数据字典 +(NSMutableDictionary *) buildDataWithJson: (NSString *) json andKey: (NSString *) key{ if( json == nil || json.length == 0) return nil; NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding]; NSError *err; NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; if(err || dict == nil) { NSLog(@"json解析失败:%@",err); [U3DAnalytics onTchErrorEvent:@"json_error" andRaw:json andOther:@""]; return nil; } // ---------- 太极数据校验 ------------- if(key == TchAdRevRoas001){ // tch 001 参数修复 [U3DAnalytics fixTchParams: dict andJson:json andMaxValue:tch001MaxValue]; } else if(key == TchAdRevRoas02){ // tch 02 参数修复 [U3DAnalytics fixTchParams: dict andJson:json andMaxValue:tch02MaxValue]; } return dict; } // 修复自打点数据 +(void) fixTchParams: (NSMutableDictionary *)dict andJson: (NSString *)json andMaxValue: (double) targetValue { // --- 保存 raw数据 --- [dict setValue: json forKey:@"raw"]; id _platform = [dict objectForKey:@"ad_platform"]; id _value = [dict objectForKey:@"value"]; if([U3DAnalytics isNullObject: _platform]) { // 不存在 ad_paltform [U3DAnalytics onTchErrorEvent:@"no_ad_platform" andRaw:json andOther:@""]; } else { // 非IAP订单 if(![[_platform stringValue] isEqual:@"appstore"] ) { if([U3DAnalytics isNullObject: _value] ){ [dict setValue: [NSNumber numberWithDouble: targetValue] forKey:@"value"]; [U3DAnalytics onTchErrorEvent:@"no_value" andRaw:json andOther:@""]; } else { if([_value doubleValue] < targetValue){ [dict setValue: [NSNumber numberWithDouble: targetValue] forKey:@"value"]; [U3DAnalytics onTchErrorEvent:@"value_error" andRaw:json andOther: [_value stringValue]]; } } } } } // 向Unity发送数据 +(void) sendMessage: (NSString *)msg { // NSLog(@"--- unityInitSDK222: %@:%@", gameObjectName, callbackName); if(gameObjectName != nil && callbackName != nil){ char *t1 = [U3DAnalytics finalChar: gameObjectName]; char *t2 = [U3DAnalytics finalChar: callbackName]; char *t3 = [U3DAnalytics finalChar: msg]; UnitySendMessage(t1, t2, t3); } } // 上报 tch_error 事件 +(void) onTchErrorEvent:(NSString *) evtName andRaw: (NSString *)raw andOther: (NSString *) other{ NSString *json = [NSString stringWithFormat:@"{\"event\":\"%@\", \"raw\":\"%@\", \"other\":\"%@\"}", evtName, raw, other]; [GuruAnalytics logEvent:TchError parameters:[U3DAnalytics buildDataWithJson:json andKey:evtName]]; } // 对象判空 + (BOOL)isNullObject:(__kindof id) obj{ if(!obj){ return YES; } if(obj == NULL){ return YES; } if([obj isEqual:[NSNull null]]){ return YES; } return NO; } //--------------------------- @end //============================ UNITY PUBLIC API ============================ extern "C" { // 请求GDPR void unityInitAnalytics(const char *appId, const char *deviceInfo, bool isDebug, const char *baseUrl, const char *uploadIpAddressStr) { // NSLog(@"--- [iOS] init Analytics libs"); [GuruAnalytics initializeLibWithUploadPeriodInSecond:kUploadPeriodInSecond batchLimit:kBatchLimit eventExpiredSeconds:kEventExpiredSeconds initializeTimeout:kInitializeTimeout saasXAPPID:[U3DAnalytics charToString:appId] saasXDEVICEINFO:[U3DAnalytics charToString:deviceInfo] loggerDebug:isDebug]; // 设置 baseUrl [U3DAnalytics setBaseUrl:baseUrl]; // 设置 uploadIpAddress // TODO: 当前的版本并不支持 uploadIpAddress, 后面的版本将 uploadIpAddressStr 转化为 Array 传入接口 } void unityInitCallback(const char *gameObject, const char *method){ // NSLog(@"--- unityInitSDK111: %s:%s", gameObject, method); gameObjectName = [NSString stringWithUTF8String:gameObject]; callbackName = [NSString stringWithUTF8String:method]; } // 设置用户ID void unitySetUserID(const char *uid){ [GuruAnalytics setUserID:[U3DAnalytics charToString:uid]]; } // 设置用户ID void unitySetScreen(const char *screenName){ [GuruAnalytics setScreen:[U3DAnalytics charToString:screenName]]; } // 设置用户ADID void unitySetAdId(const char *adId){ [GuruAnalytics setAdId:[U3DAnalytics charToString:adId]]; } // 设置用户AdjustID void unitySetAdjustID(const char *adjustId){ [GuruAnalytics setAdjustId:[U3DAnalytics charToString:adjustId]]; } // 设置用户FirebaseID void unitySetFirebaseId(const char *firebaseId){ [GuruAnalytics setFirebaseId:[U3DAnalytics charToString:firebaseId]]; } // 设置用户设备ID void unitySetDeviceId(const char *did){ [GuruAnalytics setDeviceId:[U3DAnalytics charToString:did]]; } // 设置用户属性 void unitySetUserProperty(const char *key, const char *value){ [GuruAnalytics setUserProperty:[U3DAnalytics charToString:value] forName:[U3DAnalytics charToString:key]]; } // 上报事件 void unityLogEvent(const char *key, const char *data){ NSString *evtName = [U3DAnalytics charToString:key]; NSString *json = [U3DAnalytics charToString:data]; [GuruAnalytics logEvent: evtName parameters:[U3DAnalytics buildDataWithJson:json andKey:evtName]]; // JSON转换 } // 上报事件 void unitySetTch02Value(const double value){ [U3DAnalytics setTch02MaxValue:value]; } // 打点事件成功率 void unityReportEventRate(void){ [U3DAnalytics getEventsStatistics]; // 上报事件总量 [GuruAnalytics setUserProperty:[NSString stringWithFormat:@"%d", eventCountAll] forName:@"lgd"]; // 上报成功数量 [GuruAnalytics setUserProperty:[NSString stringWithFormat:@"%d", eventCountUploaded] forName:@"uld"]; // [GuruAnalytics debug_eventsStatistics:^(NSInteger uploadedEventsCount, NSInteger loggedEventsCount) { // // // 上报事件总量 // [GuruAnalytics setUserProperty:[NSString stringWithFormat:@"%ld", loggedEventsCount] // forName:@"lgd"]; // // 上报成功数量 // [GuruAnalytics setUserProperty:[NSString stringWithFormat:@"%ld", uploadedEventsCount] // forName:@"uld"]; // }]; } // 注册内部日志 Error 监听 void unitySetEnableErrorLog(bool value){ [U3DAnalytics setEnableErrorLog:value]; } // 上报事件数量总数 int unityGetEventsCountAll(){ return eventCountAll; } // 上报事件数量成功数量 int unityGetEventsCountUploaded(){ return eventCountUploaded; } }