caid
parent
e174ec54d4
commit
92758032be
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
public class DeviceIdHandle
|
||||
|
|
@ -18,6 +19,8 @@ public class DeviceIdHandle
|
|||
public string channel;
|
||||
public string package_name;
|
||||
public int platform;
|
||||
public string identity_key;
|
||||
public string caid_device_info_str;
|
||||
|
||||
public DeviceIdInfo()
|
||||
{
|
||||
|
|
@ -35,6 +38,7 @@ public class DeviceIdHandle
|
|||
#else
|
||||
platform = 0;
|
||||
#endif
|
||||
identity_key = SystemInfo.deviceUniqueIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,4 +200,27 @@ public class DeviceIdHandle
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 CAID 设备信息 JSON(iOS 侧采集,返回所有参数的 JSON 字符串)
|
||||
/// </summary>
|
||||
public string GetCAIDDeviceInfoJSON()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
IntPtr ptr = GetCAIDDeviceInfoJSONNative();
|
||||
if (ptr == IntPtr.Zero)
|
||||
return string.Empty;
|
||||
string result = Marshal.PtrToStringAuto(ptr);
|
||||
Marshal.FreeCoTaskMem(ptr);
|
||||
return result ?? string.Empty;
|
||||
#else
|
||||
// 非 iOS 平台返回空 JSON(可用于编辑器调试)
|
||||
return string.Empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
[DllImport("__Internal", EntryPoint = "GetCAIDDeviceInfoJSON")]
|
||||
private static extern IntPtr GetCAIDDeviceInfoJSONNative();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
#import <AdSupport/AdSupport.h>
|
||||
#import <AppTrackingTransparency/AppTrackingTransparency.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <sys/stat.h>
|
||||
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
||||
#import <CoreTelephony/CTCarrier.h>
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -12,15 +17,274 @@ void UnitySendMessage(const char* obj, const char* method, const char* msg);
|
|||
|
||||
// 辅助方法:将 NSString 转换为 C 语言字符串
|
||||
const char* ConvertNSStringToCString(NSString* nsString) {
|
||||
if (nsString == NULL) {
|
||||
if (nsString == NULL || nsString == nil) {
|
||||
return NULL;
|
||||
}
|
||||
const char* nsStringUtf8 = [nsString UTF8String];
|
||||
if (nsStringUtf8 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char* cString = (char*)malloc(strlen(nsStringUtf8) + 1);
|
||||
strcpy(cString, nsStringUtf8);
|
||||
return cString;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// CAID 设备信息采集(参见 CAID API 接入文档 v1.17)
|
||||
// ==========================================
|
||||
|
||||
#pragma mark - 6.1.1 设备启动时间
|
||||
static time_t bootSecTime() {
|
||||
struct timeval boottime;
|
||||
size_t len = sizeof(boottime);
|
||||
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
|
||||
if (sysctl(mib, 2, &boottime, &len, NULL, 0) < 0) {
|
||||
return 0;
|
||||
}
|
||||
return boottime.tv_sec;
|
||||
}
|
||||
|
||||
NSString* GetBootTimeInSec() {
|
||||
return [NSString stringWithFormat:@"%ld", bootSecTime()];
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.2 国家
|
||||
NSString* GetCountryCode() {
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
|
||||
return countryCode ?: @"";
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.3 语言
|
||||
NSString* GetLanguage() {
|
||||
NSString *language;
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
if ([[NSLocale preferredLanguages] count] > 0) {
|
||||
language = [[NSLocale preferredLanguages] objectAtIndex:0];
|
||||
} else {
|
||||
language = [locale objectForKey:NSLocaleLanguageCode];
|
||||
}
|
||||
return language ?: @"";
|
||||
}
|
||||
|
||||
#pragma mark - MD5 辅助函数(32位小写)
|
||||
NSString* MD5HexDigest(NSString *input) {
|
||||
if (!input || [input length] == 0) return @"";
|
||||
const char *cStr = [input UTF8String];
|
||||
unsigned char digest[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(cStr, (CC_LONG)strlen(cStr), digest);
|
||||
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
|
||||
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
|
||||
[output appendFormat:@"%02x", digest[i]];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.4 设备名称(小写 MD5)
|
||||
NSString* GetCAIDDeviceName() {
|
||||
NSString *deviceName = [[UIDevice currentDevice] name];
|
||||
if ([deviceName length] == 0) {
|
||||
return nil;
|
||||
}
|
||||
return MD5HexDigest(deviceName);
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.5 系统版本
|
||||
NSString* GetSystemVersion() {
|
||||
return [[UIDevice currentDevice] systemVersion];
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.6 设备 Machine
|
||||
static NSString* getSystemHardwareByName(const char *typeSpecifier) {
|
||||
size_t size;
|
||||
sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
|
||||
char *answer = malloc(size);
|
||||
if (!answer) return @"";
|
||||
sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
|
||||
NSString *results = [NSString stringWithUTF8String:answer];
|
||||
free(answer);
|
||||
return results ?: @"";
|
||||
}
|
||||
|
||||
NSString* GetMachine() {
|
||||
NSString *machine = getSystemHardwareByName("hw.machine");
|
||||
return machine ?: @"";
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.7 运营商
|
||||
NSString* GetCarrierInfo() {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
return @"SIMULATOR";
|
||||
#else
|
||||
static dispatch_queue_t _queue;
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
_queue = dispatch_queue_create("com.carr.devicehelper", NULL);
|
||||
});
|
||||
|
||||
__block NSString *carr = nil;
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
|
||||
dispatch_async(_queue, ^{
|
||||
CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
|
||||
CTCarrier *carrier = nil;
|
||||
|
||||
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 12.1) {
|
||||
if ([info respondsToSelector:@selector(serviceSubscriberCellularProviders)]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
NSArray *carrierKeysArray = [info.serviceSubscriberCellularProviders.allKeys
|
||||
sortedArrayUsingSelector:@selector(compare:)];
|
||||
carrier = info.serviceSubscriberCellularProviders[carrierKeysArray.firstObject];
|
||||
if (!carrier.mobileNetworkCode) {
|
||||
carrier = info.serviceSubscriberCellularProviders[carrierKeysArray.lastObject];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
if (!carrier) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
carrier = info.subscriberCellularProvider;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
if (carrier != nil) {
|
||||
NSString *networkCode = [carrier mobileNetworkCode];
|
||||
NSString *countryCode = [carrier mobileCountryCode];
|
||||
|
||||
if (countryCode && [countryCode isEqualToString:@"460"] && networkCode) {
|
||||
if ([networkCode isEqualToString:@"00"] ||
|
||||
[networkCode isEqualToString:@"02"] ||
|
||||
[networkCode isEqualToString:@"07"] ||
|
||||
[networkCode isEqualToString:@"08"]) {
|
||||
carr = @"中国移动";
|
||||
} else if ([networkCode isEqualToString:@"01"] ||
|
||||
[networkCode isEqualToString:@"06"] ||
|
||||
[networkCode isEqualToString:@"09"]) {
|
||||
carr = @"中国联通";
|
||||
} else if ([networkCode isEqualToString:@"03"] ||
|
||||
[networkCode isEqualToString:@"05"] ||
|
||||
[networkCode isEqualToString:@"11"]) {
|
||||
carr = @"中国电信";
|
||||
} else if ([networkCode isEqualToString:@"04"]) {
|
||||
carr = @"中国卫通";
|
||||
} else if ([networkCode isEqualToString:@"20"]) {
|
||||
carr = @"中国铁通";
|
||||
}
|
||||
} else {
|
||||
carr = [carrier.carrierName copy];
|
||||
}
|
||||
}
|
||||
|
||||
if (carr.length <= 0) {
|
||||
carr = @"unknown";
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
});
|
||||
|
||||
dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC);
|
||||
dispatch_semaphore_wait(semaphore, t);
|
||||
return [carr copy] ?: @"unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.8 物理内存容量
|
||||
NSString* GetMemory() {
|
||||
return [NSString stringWithFormat:@"%lld", [NSProcessInfo processInfo].physicalMemory];
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.9 硬盘容量
|
||||
NSString* GetDisk() {
|
||||
int64_t space = -1;
|
||||
NSError *error = nil;
|
||||
NSDictionary *attrs = [[NSFileManager defaultManager]
|
||||
attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
|
||||
if (!error) {
|
||||
space = [[attrs objectForKey:NSFileSystemSize] longLongValue];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%lld", space];
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.10 系统更新时间
|
||||
NSString* GetSysFileTime() {
|
||||
NSString *result = nil;
|
||||
// Base64 解码: /var/mobile/Library/UserConfigurationProfiles/PublicInfo/MCMeta.plist
|
||||
NSString *information = @"L3Zhci9tb2JpbGUvTGlicmFyeS9Vc2VyQ29uZmlndXJhdGlvblByb2ZpbGVzL1B1YmxpY0luZm8vTUNNZXRhLnBsaXN0";
|
||||
NSData *data = [[NSData alloc] initWithBase64EncodedString:information options:0];
|
||||
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager]
|
||||
attributesOfItemAtPath:dataString error:&error];
|
||||
if (fileAttributes) {
|
||||
id singleAttribute = [fileAttributes objectForKey:NSFileCreationDate];
|
||||
if ([singleAttribute isKindOfClass:[NSDate class]]) {
|
||||
NSDate *dataDate = (NSDate *)singleAttribute;
|
||||
result = [NSString stringWithFormat:@"%f", [dataDate timeIntervalSince1970]];
|
||||
}
|
||||
}
|
||||
return result ?: @"";
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.11 设备 Model
|
||||
NSString* GetModel() {
|
||||
NSString *model = getSystemHardwareByName("hw.model");
|
||||
return model ?: @"";
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.12 时区
|
||||
NSString* GetTimeZone() {
|
||||
NSInteger offset = [NSTimeZone systemTimeZone].secondsFromGMT;
|
||||
return [NSString stringWithFormat:@"%ld", (long)offset];
|
||||
}
|
||||
|
||||
#pragma mark - 6.1.13 设备初始化时间
|
||||
NSString* GetDeviceInitTime() {
|
||||
struct stat info;
|
||||
int result = stat("/var/mobile", &info);
|
||||
if (result != 0) {
|
||||
return @"";
|
||||
}
|
||||
struct timespec time = info.st_birthtimespec;
|
||||
return [NSString stringWithFormat:@"%ld.%09ld", time.tv_sec, time.tv_nsec];
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Unity C# 统一调用接口:返回 CAID 设备信息的 JSON 字符串
|
||||
// ==========================================
|
||||
const char* GetCAIDDeviceInfoJSON() {
|
||||
NSMutableDictionary *deviceInfo = [NSMutableDictionary dictionary];
|
||||
|
||||
deviceInfo[@"bootTimeInSec"] = GetBootTimeInSec() ?: @"";
|
||||
deviceInfo[@"countryCode"] = GetCountryCode() ?: @"";
|
||||
deviceInfo[@"language"] = GetLanguage() ?: @"";
|
||||
deviceInfo[@"deviceName"] = GetCAIDDeviceName() ?: @"";
|
||||
deviceInfo[@"systemVersion"] = GetSystemVersion() ?: @"";
|
||||
deviceInfo[@"machine"] = GetMachine() ?: @"";
|
||||
deviceInfo[@"carrierInfo"] = GetCarrierInfo() ?: @"";
|
||||
deviceInfo[@"memory"] = GetMemory() ?: @"";
|
||||
deviceInfo[@"disk"] = GetDisk() ?: @"";
|
||||
deviceInfo[@"sysFileTime"] = GetSysFileTime() ?: @"";
|
||||
deviceInfo[@"model"] = GetModel() ?: @"";
|
||||
deviceInfo[@"timeZone"] = GetTimeZone() ?: @"";
|
||||
deviceInfo[@"deviceInitTime"] = GetDeviceInitTime() ?: @"";
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:deviceInfo options:0 error:&error];
|
||||
if (error || !jsonData) {
|
||||
return ConvertNSStringToCString(@"");
|
||||
}
|
||||
|
||||
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
return ConvertNSStringToCString(jsonString);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// IDFA / IDFV (原有方法保留)
|
||||
// ==========================================
|
||||
|
||||
// Unity 调用:请求/拉取 IDFA (保留此方法供 Unity 安全拉取数据)
|
||||
void RequestIDFA() {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
|
|
|||
|
|
@ -1,30 +1,170 @@
|
|||
# DeviceIdentifiers.m
|
||||
# DeviceHelper_iOS
|
||||
|
||||
用于向 iOS (Xcode) 原生层请求获取设备广告标识符 (IDFA) 与供应商标识符 (IDFV) 的 Unity 桥接脚本。
|
||||
Unity iOS 原生桥接插件,提供以下功能:
|
||||
|
||||
1. **IDFA** — 广告标识符(异步获取,含自动弹窗机制)
|
||||
2. **IDFV** — 供应商标识符(同步获取)
|
||||
3. **CAID 设备信息采集** — 采集 iOS 设备信息,以 JSON 字符串形式返回给 Unity C#
|
||||
|
||||
---
|
||||
|
||||
## 文件说明
|
||||
|
||||
| 文件 | 路径 |
|
||||
|------|------|
|
||||
| 源文件 | `Assets/Plugins/iOS/DeviceIdentifiers.m` |
|
||||
| C# 桥接 | 项目 `Assets/PhxhSDK/Phxh/Others/DeviceHelper.cs` |
|
||||
|
||||
---
|
||||
|
||||
## Unity C# 调用方式
|
||||
|
||||
### 1. 获取 IDFA(异步)
|
||||
|
||||
```csharp
|
||||
// IDFA 为异步获取,结果会通过 UnitySendMessage 回调到 GameLauncher.OnIDFAResult
|
||||
// 主动触发请求:
|
||||
// (代码中已实现 App 启动时自动弹窗,无需额外调用)
|
||||
// 若需手动调用:
|
||||
// (在 iOS 原生侧已暴露 RequestIDFA() 函数,可通过 DllImport 调用)
|
||||
```
|
||||
|
||||
> **回调方式**:结果通过 `UnitySendMessage("GameLauncher", "OnIDFAResult", idfaString)` 发送。
|
||||
> 确保 Unity 场景中存在名为 **GameLauncher** 的 GameObject,并挂载了 `OnIDFAResult(string idfa)` 方法。
|
||||
|
||||
### 2. 获取 IDFV(同步)
|
||||
|
||||
```csharp
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static string GetIDFV()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
IntPtr ptr = GetIDFVNative();
|
||||
string result = Marshal.PtrToStringAuto(ptr);
|
||||
Marshal.FreeCoTaskMem(ptr);
|
||||
return result;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
[DllImport("__Internal", EntryPoint = "GetIDFV")]
|
||||
private static extern IntPtr GetIDFVNative();
|
||||
#endif
|
||||
```
|
||||
|
||||
### 3. 获取 CAID 设备信息 JSON(同步)
|
||||
|
||||
```csharp
|
||||
// 直接调用 DeviceHelper.GetCAIDDeviceInfoJSON()
|
||||
// 返回 JSON 字符串,包含 CAID API 所需的全部设备参数
|
||||
string caidJson = DeviceHelper.GetCAIDDeviceInfoJSON();
|
||||
```
|
||||
|
||||
返回的 JSON 示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"bootTimeInSec": "1595643553",
|
||||
"countryCode": "CN",
|
||||
"language": "zh-Hans-CN",
|
||||
"deviceName": "e910dddb2748c36b47fcde5dd720eec1",
|
||||
"systemVersion": "14.0",
|
||||
"machine": "iPhone10,3",
|
||||
"carrierInfo": "中国移动",
|
||||
"memory": "3955589120",
|
||||
"disk": "63900340224",
|
||||
"sysFileTime": "1595214620.383940",
|
||||
"model": "D22AP",
|
||||
"timeZone": "28800",
|
||||
"deviceInitTime": "1632467920.301150749"
|
||||
}
|
||||
```
|
||||
|
||||
各字段说明参见 [CAID API 接入文档](https://caid.china-caa.org) 附录 6.1 章节。
|
||||
|
||||
#### C# 侧方法定义(已内置在 `DeviceHelper.cs` 中)
|
||||
|
||||
```csharp
|
||||
public static string GetCAIDDeviceInfoJSON()
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
IntPtr ptr = GetCAIDDeviceInfoJSONNative();
|
||||
if (ptr == IntPtr.Zero)
|
||||
return "{}";
|
||||
string result = Marshal.PtrToStringAuto(ptr);
|
||||
Marshal.FreeCoTaskMem(ptr);
|
||||
return result ?? "{}";
|
||||
#else
|
||||
return "{}";
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
[DllImport("__Internal", EntryPoint = "GetCAIDDeviceInfoJSON")]
|
||||
private static extern IntPtr GetCAIDDeviceInfoJSONNative();
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意点
|
||||
|
||||
- IDFA (广告标识符) —— 异步获取
|
||||
*授权弹窗:获取 IDFA 必须调起 iOS 系统原生的 AppTrackingTransparency 弹窗,只有在用户主动允许追踪后才可获取真实 IDFA。
|
||||
* 异步回调:由于需要等待用户交互,此过程为异步操作。
|
||||
* 回调目标:获取结果会通过 UnitySendMessage 传递给 Unity。请务必确保 Unity 场景中存在名为 GameLauncher 的 GameObject,并已挂载接收回调的方法。
|
||||
### IDFA
|
||||
|
||||
- IDFV (供应商标识符) —— 同步获取
|
||||
* 直接获取:IDFV 为设备厂商 ID,无需经过用户授权弹窗,可直接在调用时同步获取并返回结果。
|
||||
- **授权弹窗**:获取 IDFA 必须调起 iOS 系统原生的 AppTrackingTransparency 弹窗,只有在用户主动允许追踪后才可获取真实 IDFA。
|
||||
- **异步回调**:由于需要等待用户交互,此过程为异步操作。
|
||||
- **回调目标**:获取结果会通过 `UnitySendMessage` 传递给 Unity。请务必确保 Unity 场景中存在名为 `GameLauncher` 的 GameObject,并已挂载接收回调的方法。
|
||||
|
||||
- 自动触发机制 (App 启动即弹窗)
|
||||
* 默认行为:代码内实现了 onAppDidBecomeActive 监听。App 每次启动并进入活跃状态时,将自动调起 IDFA 请求弹窗。
|
||||
* 修改建议:若项目业务逻辑不需要在 App 刚启动时就弹出授权框(例如需要延迟到特定关卡或场景再弹),请在源码中手动删除或注释掉 onAppDidBecomeActive 相关的监听与函数体。
|
||||
### IDFV
|
||||
|
||||
## Xcode 工程配置须知 (重要)
|
||||
- 添加系统库:
|
||||
在 Xcode -> Target -> Build Phases -> Link Binary With Libraries 中,添加以下框架:
|
||||
* AdSupport.framework
|
||||
* AppTrackingTransparency.framework
|
||||
- **直接获取**:IDFV 为设备厂商 ID,无需经过用户授权弹窗,可直接在调用时同步获取并返回结果。
|
||||
|
||||
- 配置隐私描述:
|
||||
在 Info.plist 中必须添加跟踪权限的用途描述,否则弹窗无法显示且会导致应用崩溃或拒审:
|
||||
* Key: Privacy - Tracking Usage Description (NSUserTrackingUsageDescription)
|
||||
* Value: (请填写向用户解释为何需要追踪的文案)
|
||||
### CAID 设备信息
|
||||
|
||||
- **同步接口**:所有设备参数的采集均在当前线程同步完成,返回 JSON 字符串。
|
||||
- **注意内存释放**:C# 侧在 `Marshal.PtrToStringAuto` 后调用 `Marshal.FreeCoTaskMem` 释放原生侧 `malloc` 分配的内存。
|
||||
|
||||
### 自动触发机制(IDFA 弹窗)
|
||||
|
||||
- **默认行为**:代码内实现了 `onAppDidBecomeActive` 监听。App 每次启动并进入活跃状态时,将自动调起 IDFA 请求弹窗。
|
||||
- **修改建议**:若项目业务逻辑不需要在 App 刚启动时就弹出授权框(例如需要延迟到特定关卡或场景再弹),请在源码中手动删除或注释掉 `AutoIDFATrigger` 相关的 `+load` 与 `onAppDidBecomeActive` 实现。
|
||||
|
||||
---
|
||||
|
||||
## Xcode 工程配置须知(重要)
|
||||
|
||||
- **添加系统库**:
|
||||
在 Xcode -> Target -> Build Phases -> Link Binary With Libraries 中,添加以下框架:
|
||||
|
||||
| 框架 | 用途 |
|
||||
|------|------|
|
||||
| `AdSupport.framework` | IDFA 获取 |
|
||||
| `AppTrackingTransparency.framework` | 跟踪授权弹窗 |
|
||||
| `CoreTelephony.framework` | 运营商信息采集(CAID) |
|
||||
| `libz.tbd` | MD5 计算(CAID) |
|
||||
|
||||
- **配置隐私描述**:
|
||||
在 `Info.plist` 中必须添加跟踪权限的用途描述,否则弹窗无法显示且会导致应用崩溃或拒审:
|
||||
|
||||
- **Key**: `Privacy - Tracking Usage Description` (`NSUserTrackingUsageDescription`)
|
||||
- **Value**: 请填写向用户解释为何需要追踪的文案(如"用于提供个性化广告服务")
|
||||
|
||||
- **Unity 构建时自动添加框架**:
|
||||
若使用 Unity 2020.3+,可在 `Player Settings -> iOS -> Other Settings` 中勾选:
|
||||
|
||||
- `AdSupport.framework` → **Required**
|
||||
- `AppTrackingTransparency.framework` → **Required**
|
||||
- `CoreTelephony.framework` → **Required**
|
||||
|
||||
或在项目中添加 `TDS-Info.plist` 等相关配置(`Assets/Plugins/iOS/TDS-Info.plist`)。
|
||||
|
||||
---
|
||||
|
||||
## 版本历史
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| 1.0.0 | - | 初始版本:IDFA / IDFV 获取 + 自动弹窗 + CAID 设备信息采集 |
|
||||
|
|
|
|||
Loading…
Reference in New Issue