#if UNITY_IOS using System; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; using UnityEditor.iOS.Xcode.Extensions; using UnityEngine; namespace JustTrack { public static class XcodeProjectModifier { private const string UseFrameworksPodfileLine = "use_frameworks!"; private const string UseFrameworksDynamicPodfileLine = "use_frameworks! :linkage => :dynamic"; private const string UseFrameworksStaticPodfileLine = "use_frameworks! :linkage => :static"; private static readonly string[] VendoredFrameworkPods = { "JustTrackSDK", "JustTrackSDKAppLovinAdapter", "JustTrackSDKFirebaseAdapter", "JustTrackSDKGoogleOdmAdapter", "JustTrackSDKIronSourceAdapter", "JustTrackSDKUnityAdsAdapter", }; [PostProcessBuild(100)] public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath) { if (buildTarget != BuildTarget.iOS) { return; } JustTrackSettings settings = JustTrackUtils.GetSettings(); if (settings == null) { return; } ModifyPbxProj(buildPath, settings); } private static void ModifyPbxProj(string buildPath, JustTrackSettings settings) { string pbxProjectPath = buildPath + "/Unity-iPhone.xcodeproj/project.pbxproj"; PBXProject pbxProject = new PBXProject(); pbxProject.ReadFromFile(pbxProjectPath); string frameworkGuid = pbxProject.GetUnityFrameworkTargetGuid(); string mainTargetGuid = pbxProject.GetUnityMainTargetGuid(); ConfigureSwiftCompilationConditions(pbxProject, frameworkGuid, settings); ConfigureFrameworkLinkage(pbxProject, frameworkGuid, mainTargetGuid, buildPath); pbxProject.WriteToFile(pbxProjectPath); } private static void ConfigureSwiftCompilationConditions(PBXProject pbxProject, string frameworkGuid, JustTrackSettings settings) { string propertyName = "SWIFT_ACTIVE_COMPILATION_CONDITIONS"; string propertyValue = pbxProject.GetBuildPropertyForConfig(frameworkGuid, propertyName) + " JUSTTRACK_UNITY"; if (settings.IosAppLovinIntegration) { propertyValue += " JUSTTRACK_UNITY_APPLOVIN"; } if (settings.IosFirebaseIntegration) { propertyValue += " JUSTTRACK_UNITY_FIREBASE"; } if (settings.IosIronSourceIntegration) { propertyValue += " JUSTTRACK_UNITY_IRONSOURCE"; } if (settings.IosUnityAdsIntegration) { propertyValue += " JUSTTRACK_UNITY_UNITYADS"; } if (settings.IosGoogleOdmIntegration) { propertyValue += " JUSTTRACK_UNITY_GOOGLE_ODM"; } pbxProject.SetBuildProperty(frameworkGuid, propertyName, propertyValue.Trim()); } /// /// Configures the Xcode project based on the framework linkage type detected in the Podfile. /// When static linkage is used, additional build settings are required for Swift compatibility. /// private static void ConfigureFrameworkLinkage(PBXProject pbxProject, string frameworkGuid, string mainTargetGuid, string buildPath) { bool isStaticLinkage = IsStaticFrameworkLinkage(buildPath); if (isStaticLinkage) { Debug.Log("[JustTrack] Static framework linkage detected. Configuring Xcode project for Swift compatibility."); pbxProject.SetBuildProperty(mainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES"); pbxProject.SetBuildProperty(frameworkGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "NO"); EmbedVendoredFrameworks(pbxProject, mainTargetGuid, buildPath); } else { Debug.Log("[JustTrack] Dynamic framework linkage detected. Using default Xcode project configuration."); } pbxProject.SetBuildProperty(frameworkGuid, "CLANG_ENABLE_MODULES", "YES"); } /// /// Embeds vendored dynamic JustTrack XCFrameworks into the main target. /// This is necessary when static linkage is used because CocoaPods does not /// automatically embed vendored dynamic frameworks in static linkage mode. /// private static void EmbedVendoredFrameworks(PBXProject pbxProject, string mainTargetGuid, string buildPath) { string podsDir = Path.Combine(buildPath, "Pods"); if (!Directory.Exists(podsDir)) { Debug.LogWarning("[JustTrack] Pods directory not found. Cannot embed vendored frameworks."); return; } foreach (string podName in VendoredFrameworkPods) { string xcframeworkPath = Path.Combine(podsDir, podName, podName + ".xcframework"); if (!Directory.Exists(xcframeworkPath)) { continue; } string relativePath = "Pods/" + podName + "/" + podName + ".xcframework"; string projectPath = "Frameworks/" + podName + ".xcframework"; string fileGuid = pbxProject.AddFile(relativePath, projectPath, PBXSourceTree.Source); PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid); Debug.Log("[JustTrack] Embedded " + podName + ".xcframework in main target for static linkage compatibility."); } } /// /// Determines if the Podfile uses static framework linkage. /// Checks for 'use_frameworks! :linkage => :static' directive. /// private static bool IsStaticFrameworkLinkage(string buildPath) { string podfilePath = Path.Combine(buildPath, "Podfile"); if (!File.Exists(podfilePath)) { Debug.Log("[JustTrack] Podfile not found, assuming dynamic linkage."); return false; } try { string[] lines = File.ReadAllLines(podfilePath); int useFrameworksStaticLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksStaticPodfileLine)); if (useFrameworksStaticLineIndex == -1) { return false; } int useFrameworksLineIndex = Array.FindIndex(lines, line => line.Trim() == UseFrameworksPodfileLine); int useFrameworksDynamicLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksDynamicPodfileLine)); return useFrameworksLineIndex < useFrameworksStaticLineIndex && useFrameworksDynamicLineIndex < useFrameworksStaticLineIndex; } catch (Exception ex) { Debug.LogWarning("[JustTrack] Failed to read Podfile: " + ex.Message + ". Assuming dynamic linkage."); return false; } } } } #endif