fix: repair iOS ad SDK framework search paths

This commit is contained in:
2026-06-13 06:28:03 +08:00
parent 96364aab18
commit 8dc806c1eb
3 changed files with 283 additions and 30 deletions

View File

@@ -1,3 +1,11 @@
# [1.0.3]
### 修复
* iOS 导出后自动修复 `UnityFramework` target 的广告 SDK `FRAMEWORK_SEARCH_PATHS` / `HEADER_SEARCH_PATHS`,覆盖 Dirichlet、CSJ、GDT、DRA、GDTMobSDK、Tquic、BUAdSDK 的真实 xcframework slice。
* iOS 导出后清理 CocoaPods `Pods-UnityFramework*.xcconfig` 中的 `XCFrameworkIntermediates` 搜索路径,避免 Xcode 真机 Play 时出现 framework not found 或 search path not found。
* 保持签名配置由业务工程负责,广告模块不写入 Team、provisioning profile 或 code signing 设置。
# [1.0.1] # [1.0.1]
### 调整 ### 调整

View File

@@ -36,6 +36,9 @@ namespace Dirichlet.Mediation.Editor
private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION"; private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION";
private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion"; private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion";
private const string PrefKeyATTDescription = "Dirichlet.iOS.TrackingUsageDescription"; private const string PrefKeyATTDescription = "Dirichlet.iOS.TrackingUsageDescription";
private const string IOSDeviceSlice = "ios-arm64";
private const string TquicIOSDeviceSlice = "ios-arm64_armv7";
private static readonly string[] GDTDynamicFrameworkNames = { "GDTMobSDK.framework", "Tquic.framework" };
/// <summary> /// <summary>
/// 解析出的 target 信息 /// 解析出的 target 信息
@@ -78,8 +81,8 @@ namespace Dirichlet.Mediation.Editor
// 4. Run pod install // 4. Run pod install
RunPodInstall(pathToBuiltProject); RunPodInstall(pathToBuiltProject);
// 5. Embed GDT dynamic frameworks into app target (must run after pod install) // 5. Fix ad SDK framework/header search paths and embed GDT dynamic frameworks (must run after pod install)
EmbedGDTDynamicFrameworks(pathToBuiltProject, targetInfo); FixAdSDKIOSFrameworkConfiguration(pathToBuiltProject, targetInfo);
Debug.Log("[DirichletMediation] iOS post-process completed successfully."); Debug.Log("[DirichletMediation] iOS post-process completed successfully.");
} }
@@ -517,20 +520,16 @@ namespace Dirichlet.Mediation.Editor
} }
/// <summary> /// <summary>
/// GDTMobSDK 提供的是预编译动态库GDTMobSDK.framework, Tquic.framework /// UnityFramework 链接阶段需要显式搜索广告 SDK xcframework 内部的真机 slice
/// 必须 embed 到 app bundle 中才能在运行时加载 /// 否则 Xcode 可能报 ld: framework 'GDTMobSDK' not found 或 XCFrameworkIntermediates 搜索路径缺失
/// 由于所有 pods 都在 Framework target 上CocoaPods 不会自动 embed 到 app target /// 同时 GDTMobSDK 提供的是预编译动态库GDTMobSDK.framework, Tquic.framework
/// 需要在 pod install 之后手动处理。 /// 必须 embed 到 app bundle 中才能在运行时加载。由于所有 pods 都在 Framework target 上,
/// CocoaPods 不会自动 embed 到 app target需要在 pod install 之后手动处理。
/// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。 /// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。
/// </summary> /// </summary>
private static void EmbedGDTDynamicFrameworks(string projectPath, TargetInfo targetInfo) private static void FixAdSDKIOSFrameworkConfiguration(string projectPath, TargetInfo targetInfo)
{ {
var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true); var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true);
if (!enableGdt)
{
Debug.Log("[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding");
return;
}
var xcodeProjectName = DetectXcodeProjectName(projectPath); var xcodeProjectName = DetectXcodeProjectName(projectPath);
var projectFilePath = Path.Combine(projectPath, $"{xcodeProjectName}.xcodeproj/project.pbxproj"); var projectFilePath = Path.Combine(projectPath, $"{xcodeProjectName}.xcodeproj/project.pbxproj");
@@ -543,35 +542,281 @@ namespace Dirichlet.Mediation.Editor
mainTargetGuid = pbxProject.TargetGuidByName(targetInfo.AppTargetName); mainTargetGuid = pbxProject.TargetGuidByName(targetInfo.AppTargetName);
} }
// GDTMobSDK 的动态框架列表 var targetGuid = ResolveUnityFrameworkTargetGuid(pbxProject, targetInfo);
var dynamicFrameworkNames = new[] { "GDTMobSDK.framework", "Tquic.framework" }; var frameworkSearchPaths = GetAdSDKFrameworkSearchPaths();
var headerSearchPaths = GetAdSDKHeaderSearchPaths(frameworkSearchPaths);
var searchPathsAdded = AddAdSDKSearchPaths(pbxProject, targetGuid, frameworkSearchPaths, headerSearchPaths);
PatchPodsXCConfigSearchPaths(projectPath, targetInfo, frameworkSearchPaths, headerSearchPaths);
var podsDir = Path.Combine(projectPath, "Pods"); var podsDir = Path.Combine(projectPath, "Pods");
var embedded = 0; var embedded = 0;
foreach (var frameworkName in dynamicFrameworkNames) if (enableGdt)
{ {
var frameworkPath = FindDynamicFramework(podsDir, frameworkName); foreach (var frameworkName in GDTDynamicFrameworkNames)
if (string.IsNullOrEmpty(frameworkPath)) {
var frameworkPath = FindDynamicFramework(podsDir, frameworkName);
if (string.IsNullOrEmpty(frameworkPath))
{
Debug.LogWarning($"[DirichletMediation] Dynamic framework not found: {frameworkName}");
continue;
}
var relativePath = frameworkPath.StartsWith(projectPath)
? frameworkPath.Substring(projectPath.Length + 1)
: frameworkPath;
var fileGuid = pbxProject.AddFile(relativePath, "Frameworks/" + frameworkName);
PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid);
embedded++;
Debug.Log($"[DirichletMediation] Embedded dynamic framework: {frameworkName}");
}
}
else
{
Debug.Log("[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding");
}
pbxProject.WriteToFile(projectFilePath);
if (searchPathsAdded)
{
Debug.Log($"[DirichletMediation] Added ad SDK framework/header search paths to {targetInfo.FrameworkTargetName}");
}
Debug.Log($"[DirichletMediation] Embedded {embedded} GDT dynamic frameworks into {targetInfo.AppTargetName}");
}
private static string ResolveUnityFrameworkTargetGuid(PBXProject pbxProject, TargetInfo targetInfo)
{
#if UNITY_2019_3_OR_NEWER
var targetGuid = pbxProject.GetUnityFrameworkTargetGuid();
#else
var targetGuid = targetInfo.FrameworkTargetGuid;
#endif
if (string.IsNullOrEmpty(targetGuid))
{
targetGuid = targetInfo.FrameworkTargetGuid;
}
if (string.IsNullOrEmpty(targetGuid))
{
targetGuid = pbxProject.TargetGuidByName(targetInfo.FrameworkTargetName);
}
return targetGuid;
}
private static bool AddAdSDKSearchPaths(PBXProject pbxProject, string targetGuid, string[] frameworkSearchPaths, string[] headerSearchPaths)
{
if (string.IsNullOrEmpty(targetGuid))
{
Debug.LogWarning("[DirichletMediation] UnityFramework target GUID is empty, skipping ad SDK search paths");
return false;
}
pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
foreach (var searchPath in frameworkSearchPaths)
{
pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", searchPath);
}
pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(inherited)");
foreach (var searchPath in headerSearchPaths)
{
pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", searchPath);
}
return true;
}
private static void PatchPodsXCConfigSearchPaths(
string projectPath,
TargetInfo targetInfo,
string[] frameworkSearchPaths,
string[] headerSearchPaths)
{
var targetSupportRoot = Path.Combine(projectPath, "Pods", "Target Support Files");
if (!Directory.Exists(targetSupportRoot))
{
Debug.LogWarning("[DirichletMediation] CocoaPods target support files not found, skipping xcconfig search path patch");
return;
}
var targetPrefix = "Pods-" + targetInfo.FrameworkTargetName;
var xcconfigFiles = Directory.GetFiles(targetSupportRoot, "*.xcconfig", SearchOption.AllDirectories)
.Where(path => Path.GetFileNameWithoutExtension(path).StartsWith(targetPrefix, System.StringComparison.OrdinalIgnoreCase))
.ToArray();
if (xcconfigFiles.Length == 0)
{
Debug.LogWarning($"[DirichletMediation] No CocoaPods xcconfig found for {targetInfo.FrameworkTargetName}, skipping search path patch");
return;
}
var patched = 0;
foreach (var xcconfigPath in xcconfigFiles)
{
var lines = File.ReadAllLines(xcconfigPath).ToList();
var changed = UpsertXCConfigBuildSetting(lines, "FRAMEWORK_SEARCH_PATHS", frameworkSearchPaths);
changed |= UpsertXCConfigBuildSetting(lines, "HEADER_SEARCH_PATHS", headerSearchPaths);
if (!changed)
{ {
Debug.LogWarning($"[DirichletMediation] Dynamic framework not found: {frameworkName}");
continue; continue;
} }
var relativePath = frameworkPath.StartsWith(projectPath) File.WriteAllLines(xcconfigPath, lines);
? frameworkPath.Substring(projectPath.Length + 1) patched++;
: frameworkPath;
var fileGuid = pbxProject.AddFile(relativePath, "Frameworks/" + frameworkName);
PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid);
embedded++;
Debug.Log($"[DirichletMediation] Embedded dynamic framework: {frameworkName}");
} }
if (embedded > 0) Debug.Log($"[DirichletMediation] Patched {patched} CocoaPods xcconfig search path file(s) for {targetInfo.FrameworkTargetName}");
}
private static bool UpsertXCConfigBuildSetting(System.Collections.Generic.List<string> lines, string propertyName, string[] searchPaths)
{
var insertIndex = -1;
var originalLines = new System.Collections.Generic.List<string>();
var tokens = new System.Collections.Generic.List<string>();
for (var i = lines.Count - 1; i >= 0; i--)
{ {
pbxProject.WriteToFile(projectFilePath); if (!IsXCConfigBuildSettingLine(lines[i], propertyName))
Debug.Log($"[DirichletMediation] Embedded {embedded} GDT dynamic frameworks into {targetInfo.AppTargetName}"); {
continue;
}
insertIndex = i;
originalLines.Add(lines[i]);
CollectXCConfigBuildSettingTokens(lines[i], tokens);
lines.RemoveAt(i);
} }
if (!ContainsXCConfigToken(tokens, "$(inherited)"))
{
tokens.Insert(0, "$(inherited)");
}
foreach (var searchPath in searchPaths)
{
AddXCConfigTokenIfMissing(tokens, searchPath);
}
var newLine = $"{propertyName} = {string.Join(" ", tokens)}";
if (insertIndex < 0)
{
lines.Add(newLine);
return true;
}
lines.Insert(insertIndex, newLine);
return originalLines.Count != 1 || originalLines[0] != newLine;
}
private static void CollectXCConfigBuildSettingTokens(string line, System.Collections.Generic.List<string> tokens)
{
var separatorIndex = line.IndexOf('=');
if (separatorIndex < 0 || separatorIndex >= line.Length - 1)
{
return;
}
var value = line.Substring(separatorIndex + 1).Trim();
var lineTokens = value.Split(new[] { ' ', '\t' }, System.StringSplitOptions.RemoveEmptyEntries);
foreach (var token in lineTokens)
{
if (token.IndexOf("XCFrameworkIntermediates", System.StringComparison.OrdinalIgnoreCase) >= 0)
{
continue;
}
AddXCConfigTokenIfMissing(tokens, token);
}
}
private static bool ContainsXCConfigToken(System.Collections.Generic.List<string> tokens, string value)
{
var normalizedValue = NormalizeXCConfigToken(value);
return tokens.Any(token => NormalizeXCConfigToken(token) == normalizedValue);
}
private static void AddXCConfigTokenIfMissing(System.Collections.Generic.List<string> tokens, string value)
{
if (!ContainsXCConfigToken(tokens, value))
{
tokens.Add(value);
}
}
private static string NormalizeXCConfigToken(string token)
{
return (token ?? string.Empty).Trim().Trim('"');
}
private static bool IsXCConfigBuildSettingLine(string line, string propertyName)
{
if (string.IsNullOrWhiteSpace(line))
{
return false;
}
var trimmed = line.TrimStart();
return trimmed.StartsWith(propertyName + " =", System.StringComparison.Ordinal)
|| trimmed.StartsWith(propertyName + "=", System.StringComparison.Ordinal);
}
private static string[] GetAdSDKFrameworkSearchPaths()
{
var sdkVersion = ResolveIOSSDKVersion();
var enableCsj = EditorPrefs.GetBool("Dirichlet.iOS.EnableCSJ", true);
var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true);
var paths = new System.Collections.Generic.List<string>
{
VersionedDirichletFrameworkSearchPath("DirichletAdSDK", sdkVersion),
VersionedDirichletFrameworkSearchPath("DirichletCoreSDK", sdkVersion),
VersionedDirichletFrameworkSearchPath("DirichletMediationSDK", sdkVersion),
VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterDRA", sdkVersion)
};
if (enableCsj)
{
paths.Add(VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterCSJ", sdkVersion));
paths.Add("$(PODS_ROOT)/Ads-CN/SDK/BUAdSDK.xcframework/" + IOSDeviceSlice);
}
if (enableGdt)
{
paths.Add(VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterGDT", sdkVersion));
paths.Add("$(PODS_ROOT)/GDTMobSDK/GDTFramework/GDTMobSDK.xcframework/" + IOSDeviceSlice);
paths.Add("$(PODS_ROOT)/GDTMobSDK/GDTFramework/Tquic.xcframework/" + TquicIOSDeviceSlice);
}
return paths.ToArray();
}
private static string[] GetAdSDKHeaderSearchPaths(string[] frameworkSearchPaths)
{
return frameworkSearchPaths
.Select(ToFrameworkHeaderSearchPath)
.Where(path => !string.IsNullOrEmpty(path))
.ToArray();
}
private static string VersionedDirichletFrameworkSearchPath(string frameworkName, string sdkVersion)
{
return $"$(PODS_ROOT)/{frameworkName}/{frameworkName}-{sdkVersion}/{frameworkName}.xcframework/{IOSDeviceSlice}";
}
private static string ToFrameworkHeaderSearchPath(string frameworkSearchPath)
{
var parts = frameworkSearchPath.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
var xcframeworkPart = parts.LastOrDefault(part => part.EndsWith(".xcframework", System.StringComparison.OrdinalIgnoreCase));
if (string.IsNullOrEmpty(xcframeworkPart))
{
return null;
}
var frameworkName = xcframeworkPart.Substring(0, xcframeworkPart.Length - ".xcframework".Length);
return $"{frameworkSearchPath}/{frameworkName}.framework/Headers";
} }
/// <summary> /// <summary>

View File

@@ -2,7 +2,7 @@
"name": "com.commercialization.tapadn", "name": "com.commercialization.tapadn",
"displayName": "Commercialization.tapadn", "displayName": "Commercialization.tapadn",
"description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.", "description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.",
"version": "1.0.2", "version": "1.0.3",
"unity": "2022.3", "unity": "2022.3",
"license": "MIT", "license": "MIT",
"repository": { "repository": {