123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- //
- // MaxIntegrationManager.cs
- // AppLovin MAX Unity Plugin
- //
- // Created by Santosh Bagadi on 8/29/19.
- // Copyright © 2019 AppLovin. All rights reserved.
- //
- #if UNITY_IOS || UNITY_IPHONE
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- using UnityEditor;
- using UnityEditor.Callbacks;
- #if UNITY_2019_3_OR_NEWER
- using UnityEditor.iOS.Xcode.Extensions;
- #endif
- using UnityEditor.iOS.Xcode;
- using UnityEditor.PackageManager;
- using UnityEngine;
- using UnityEngine.Networking;
- namespace AppLovinMax.Scripts.IntegrationManager.Editor
- {
- [Serializable]
- public class SkAdNetworkData
- {
- [SerializeField] public string[] SkAdNetworkIds;
- }
- public class AppLovinPostProcessiOS
- {
- private const string OutputFileName = "AppLovinQualityServiceSetup.rb";
- #if !UNITY_2019_3_OR_NEWER
- private const string UnityMainTargetName = "Unity-iPhone";
- #endif
- // Use a priority of 90 to have AppLovin embed frameworks after Pods are installed (EDM finishes installing Pods at priority 60) and before Firebase Crashlytics runs their scripts (at priority 100).
- private const int AppLovinEmbedFrameworksPriority = 90;
- private const string TargetUnityIphonePodfileLine = "target 'Unity-iPhone' do";
- private const string UseFrameworksPodfileLine = "use_frameworks!";
- private const string UseFrameworksDynamicPodfileLine = "use_frameworks! :linkage => :dynamic";
- private const string UseFrameworksStaticPodfileLine = "use_frameworks! :linkage => :static";
- private const string ResourcesDirectoryName = "Resources";
- private const string AppLovinMaxResourcesDirectoryName = "AppLovinMAXResources";
- private const string AppLovinAdvertisingAttributionEndpoint = "https://postbacks-app.com";
- private const string AppLovinSettingsPlistFileName = "AppLovin-Settings.plist";
- private const string KeySdkKey = "SdkKey";
- private const string AppLovinVerboseLoggingOnKey = "AppLovinVerboseLoggingOn";
- private const string KeyConsentFlowInfo = "ConsentFlowInfo";
- private const string KeyConsentFlowEnabled = "ConsentFlowEnabled";
- private const string KeyConsentFlowTermsOfService = "ConsentFlowTermsOfService";
- private const string KeyConsentFlowPrivacyPolicy = "ConsentFlowPrivacyPolicy";
- private const string KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR = "ConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR";
- private const string KeyConsentFlowDebugUserGeography = "ConsentFlowDebugUserGeography";
- private const string KeyAppLovinSdkKeyToRemove = "AppLovinSdkKey";
- private static readonly Regex PodfilePodLineRegex = new Regex("pod \'([^\']*)\'");
- /// <summary>
- /// Adds AppLovin Quality Service to the iOS project once the project has been exported.
- ///
- /// 1. Downloads the Quality Service ruby script.
- /// 2. Runs the script using Ruby which integrates AppLovin Quality Service to the project.
- /// </summary>
- [PostProcessBuild(AppLovinPreProcess.CallbackOrder)] // We want to run Quality Service script last.
- public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
- {
- if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
- var sdkKey = AppLovinSettings.Instance.SdkKey;
- if (string.IsNullOrEmpty(sdkKey))
- {
- MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. SDK Key is empty. Please enter the AppLovin SDK Key in the Integration Manager.");
- return;
- }
- var outputFilePath = Path.Combine(buildPath, OutputFileName);
- // Check if Quality Service is already installed.
- if (File.Exists(outputFilePath) && Directory.Exists(Path.Combine(buildPath, "AppLovinQualityService")))
- {
- // TODO: Check if there is a way to validate if the SDK key matches the script. Else the pub can't use append when/if they change the SDK Key.
- return;
- }
- // Download the ruby script needed to install Quality Service
- var downloadHandler = new DownloadHandlerFile(outputFilePath);
- var postJson = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey);
- var bodyRaw = Encoding.UTF8.GetBytes(postJson);
- var uploadHandler = new UploadHandlerRaw(bodyRaw);
- uploadHandler.contentType = "application/json";
- using (var unityWebRequest = new UnityWebRequest("https://api2.safedk.com/v1/build/ios_setup2"))
- {
- unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
- unityWebRequest.downloadHandler = downloadHandler;
- unityWebRequest.uploadHandler = uploadHandler;
- var operation = unityWebRequest.SendWebRequest();
- // Wait for the download to complete or the request to timeout.
- while (!operation.isDone) { }
- #if UNITY_2020_1_OR_NEWER
- if (unityWebRequest.result != UnityWebRequest.Result.Success)
- #else
- if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
- #endif
- {
- MaxSdkLogger.UserError("AppLovin Quality Service installation failed. Failed to download script with error: " + unityWebRequest.error);
- return;
- }
- // Check if Ruby is installed
- var rubyVersion = AppLovinCommandLine.Run("ruby", "--version", buildPath);
- if (rubyVersion.ExitCode != 0)
- {
- MaxSdkLogger.UserError("AppLovin Quality Service installation requires Ruby. Please install Ruby, export it to your system PATH and re-export the project.");
- return;
- }
- // Ruby is installed, run `ruby AppLovinQualityServiceSetup.rb`
- var result = AppLovinCommandLine.Run("ruby", OutputFileName, buildPath);
- // Check if we have an error.
- if (result.ExitCode != 0) MaxSdkLogger.UserError("Failed to set up AppLovin Quality Service");
- MaxSdkLogger.UserDebug(result.Message);
- }
- }
- [PostProcessBuild(AppLovinEmbedFrameworksPriority)]
- public static void MaxPostProcessPbxProject(BuildTarget buildTarget, string buildPath)
- {
- var projectPath = PBXProject.GetPBXProjectPath(buildPath);
- var project = new PBXProject();
- project.ReadFromFile(projectPath);
- #if UNITY_2019_3_OR_NEWER
- var unityMainTargetGuid = project.GetUnityMainTargetGuid();
- var unityFrameworkTargetGuid = project.GetUnityFrameworkTargetGuid();
- #else
- var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
- var unityFrameworkTargetGuid = project.TargetGuidByName(UnityMainTargetName);
- #endif
- EmbedDynamicLibrariesIfNeeded(buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, "de", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, "en", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, "es", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, "fr", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, "ja", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, "ko", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, "zh-Hans", buildPath, project, unityMainTargetGuid);
- LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, "zh-Hant", buildPath, project, unityMainTargetGuid);
- AddSwiftSupport(buildPath, project, unityFrameworkTargetGuid, unityMainTargetGuid);
- AddYandexSettingsIfNeeded(project, unityMainTargetGuid);
- project.WriteToFile(projectPath);
- }
- private static void EmbedDynamicLibrariesIfNeeded(string buildPath, PBXProject project, string targetGuid)
- {
- // Check that the Pods directory exists (it might not if a publisher is building with Generate Podfile setting disabled in EDM).
- var podsDirectory = Path.Combine(buildPath, "Pods");
- if (!Directory.Exists(podsDirectory) || !ShouldEmbedDynamicLibraries(buildPath)) return;
- var dynamicLibraryPathsToEmbed = GetDynamicLibraryPathsToEmbed(podsDirectory, buildPath);
- if (dynamicLibraryPathsToEmbed == null || dynamicLibraryPathsToEmbed.Count == 0) return;
- #if UNITY_2019_3_OR_NEWER
- foreach (var dynamicLibraryPath in dynamicLibraryPathsToEmbed)
- {
- var fileGuid = project.AddFile(dynamicLibraryPath, dynamicLibraryPath);
- project.AddFileToEmbedFrameworks(targetGuid, fileGuid);
- }
- #else
- string runpathSearchPaths;
- runpathSearchPaths = project.GetBuildPropertyForAnyConfig(targetGuid, "LD_RUNPATH_SEARCH_PATHS");
- runpathSearchPaths += string.IsNullOrEmpty(runpathSearchPaths) ? "" : " ";
- // Check if runtime search paths already contains the required search paths for dynamic libraries.
- if (runpathSearchPaths.Contains("@executable_path/Frameworks")) return;
- runpathSearchPaths += "@executable_path/Frameworks";
- project.SetBuildProperty(targetGuid, "LD_RUNPATH_SEARCH_PATHS", runpathSearchPaths);
- #endif
- }
- /// <summary>
- /// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
- /// | embed | use_frameworks! (:linkage => :dynamic) | use_frameworks! :linkage => :static | `use_frameworks!` line not present |
- /// |---------------------------|------------------------------------------|---------------------------------------|--------------------------------------|
- /// | Unity-iPhone present | Do not embed dynamic libraries | Embed dynamic libraries | Do not embed dynamic libraries |
- /// | Unity-iPhone not present | Embed dynamic libraries | Embed dynamic libraries | Embed dynamic libraries |
- /// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
- /// </summary>
- /// <param name="buildPath">An iOS build path</param>
- /// <returns>Whether or not the dynamic libraries should be embedded.</returns>
- private static bool ShouldEmbedDynamicLibraries(string buildPath)
- {
- var podfilePath = Path.Combine(buildPath, "Podfile");
- if (!File.Exists(podfilePath)) return false;
- // If the Podfile doesn't have a `Unity-iPhone` target, we should embed the dynamic libraries.
- var lines = File.ReadAllLines(podfilePath);
- var containsUnityIphoneTarget = lines.Any(line => line.Contains(TargetUnityIphonePodfileLine));
- if (!containsUnityIphoneTarget) return true;
- // If the Podfile does not have a `use_frameworks! :linkage => static` line, we should not embed the dynamic libraries.
- var useFrameworksStaticLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksStaticPodfileLine));
- if (useFrameworksStaticLineIndex == -1) return false;
- // If more than one of the `use_frameworks!` lines are present, CocoaPods will use the last one.
- var useFrameworksLineIndex = Array.FindIndex(lines, line => line.Trim() == UseFrameworksPodfileLine); // Check for exact line to avoid matching `use_frameworks! :linkage => static/dynamic`
- var useFrameworksDynamicLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksDynamicPodfileLine));
- // Check if `use_frameworks! :linkage => :static` is the last line of the three. If it is, we should embed the dynamic libraries.
- return useFrameworksLineIndex < useFrameworksStaticLineIndex && useFrameworksDynamicLineIndex < useFrameworksStaticLineIndex;
- }
- private static List<string> GetDynamicLibraryPathsToEmbed(string podsDirectory, string buildPath)
- {
- var podfilePath = Path.Combine(buildPath, "Podfile");
- var dynamicLibraryFrameworksToEmbed = GetDynamicLibraryFrameworksToEmbed(podfilePath);
- return GetDynamicLibraryPathsInProjectToEmbed(podsDirectory, dynamicLibraryFrameworksToEmbed);
- }
- private static List<string> GetDynamicLibraryFrameworksToEmbed(string podfilePath)
- {
- var dynamicLibrariesToEmbed = GetDynamicLibrariesToEmbed();
- var podsInUnityIphoneTarget = GetPodNamesInUnityIphoneTarget(podfilePath);
- var dynamicLibrariesToIgnore = dynamicLibrariesToEmbed.Where(dynamicLibraryToEmbed => podsInUnityIphoneTarget.Contains(dynamicLibraryToEmbed.PodName)).ToList();
- // Determine frameworks to embed based on the dynamic libraries to embed and ignore
- var dynamicLibraryFrameworksToIgnore = dynamicLibrariesToIgnore.SelectMany(library => library.FrameworkNames).Distinct().ToList();
- return dynamicLibrariesToEmbed.SelectMany(library => library.FrameworkNames).Except(dynamicLibraryFrameworksToIgnore).Distinct().ToList();
- }
- private static List<DynamicLibraryToEmbed> GetDynamicLibrariesToEmbed()
- {
- var pluginData = AppLovinIntegrationManager.LoadPluginDataSync();
- if (pluginData == null)
- {
- MaxSdkLogger.E("Failed to load plugin data. Dynamic libraries will not be embedded.");
- return null;
- }
- // Get the dynamic libraries to embed for each network
- var librariesToAdd = pluginData.MediatedNetworks
- .Where(network => network.DynamicLibrariesToEmbed != null)
- .SelectMany(network => network.DynamicLibrariesToEmbed
- .Where(libraryToEmbed => IsRequiredNetworkVersionInstalled(libraryToEmbed, network)))
- .ToList();
- // Get the dynamic libraries to embed for AppLovin MAX
- if (pluginData.AppLovinMax.DynamicLibrariesToEmbed != null)
- {
- librariesToAdd.AddRange(pluginData.AppLovinMax.DynamicLibrariesToEmbed);
- }
- // Get the dynamic libraries to embed for third parties
- if (pluginData.ThirdPartyDynamicLibrariesToEmbed != null)
- {
- // TODO: Add version check for third party dynamic libraries.
- librariesToAdd.AddRange(pluginData.ThirdPartyDynamicLibrariesToEmbed);
- }
- return librariesToAdd;
- }
- private static List<string> GetPodNamesInUnityIphoneTarget(string podfilePath)
- {
- var lines = File.ReadAllLines(podfilePath);
- var podNamesInUnityIphone = new List<string>();
- var insideUnityIphoneTarget = false;
- foreach (var line in lines)
- {
- // Loop until we find the `target 'Unity-iPhone'` line
- if (insideUnityIphoneTarget)
- {
- if (line.Trim() == "end") break;
- if (PodfilePodLineRegex.IsMatch(line))
- {
- var podName = PodfilePodLineRegex.Match(line).Groups[1].Value;
- podNamesInUnityIphone.Add(podName);
- }
- }
- else if (line.Contains(TargetUnityIphonePodfileLine))
- {
- insideUnityIphoneTarget = true;
- }
- }
- return podNamesInUnityIphone;
- }
- private static bool IsRequiredNetworkVersionInstalled(DynamicLibraryToEmbed libraryToEmbed, Network network)
- {
- var currentIosVersion = network.CurrentVersions.Ios;
- if (string.IsNullOrEmpty(currentIosVersion)) return false;
- var minIosVersion = libraryToEmbed.MinVersion;
- var maxIosVersion = libraryToEmbed.MaxVersion;
- var greaterThanOrEqualToMinVersion = string.IsNullOrEmpty(minIosVersion) || MaxSdkUtils.CompareVersions(currentIosVersion, minIosVersion) != MaxSdkUtils.VersionComparisonResult.Lesser;
- var lessThanOrEqualToMaxVersion = string.IsNullOrEmpty(maxIosVersion) || MaxSdkUtils.CompareVersions(currentIosVersion, maxIosVersion) != MaxSdkUtils.VersionComparisonResult.Greater;
- return greaterThanOrEqualToMinVersion && lessThanOrEqualToMaxVersion;
- }
- private static List<string> GetDynamicLibraryPathsInProjectToEmbed(string podsDirectory, List<string> dynamicLibrariesToEmbed)
- {
- var dynamicLibraryPathsPresentInProject = new List<string>();
- foreach (var dynamicLibraryToSearch in dynamicLibrariesToEmbed)
- {
- // both .framework and .xcframework are directories, not files
- var directories = Directory.GetDirectories(podsDirectory, dynamicLibraryToSearch, SearchOption.AllDirectories);
- if (directories.Length <= 0) continue;
- var dynamicLibraryAbsolutePath = directories[0];
- var relativePath = GetDynamicLibraryRelativePath(dynamicLibraryAbsolutePath);
- dynamicLibraryPathsPresentInProject.Add(relativePath);
- }
- return dynamicLibraryPathsPresentInProject;
- }
- private static string GetDynamicLibraryRelativePath(string dynamicLibraryAbsolutePath)
- {
- var index = dynamicLibraryAbsolutePath.LastIndexOf("Pods", StringComparison.Ordinal);
- return dynamicLibraryAbsolutePath.Substring(index);
- }
- private static void LocalizeUserTrackingDescriptionIfNeeded(string localizedUserTrackingDescription, string localeCode, string buildPath, PBXProject project, string targetGuid)
- {
- var resourcesDirectoryPath = Path.Combine(buildPath, AppLovinMaxResourcesDirectoryName);
- var localeSpecificDirectoryName = localeCode + ".lproj";
- var localeSpecificDirectoryPath = Path.Combine(resourcesDirectoryPath, localeSpecificDirectoryName);
- var infoPlistStringsFilePath = Path.Combine(localeSpecificDirectoryPath, "InfoPlist.strings");
- // Check if localization has been disabled between builds, and remove them as needed.
- if (ShouldRemoveLocalization(localizedUserTrackingDescription))
- {
- if (!File.Exists(infoPlistStringsFilePath)) return;
- File.Delete(infoPlistStringsFilePath);
- return;
- }
- // Log an error if we detect a localization file for this language in the `Resources` directory
- var legacyResourcedDirectoryPath = Path.Combine(buildPath, ResourcesDirectoryName);
- var localeSpecificLegacyDirectoryPath = Path.Combine(legacyResourcedDirectoryPath, localeSpecificDirectoryName);
- if (Directory.Exists(localeSpecificLegacyDirectoryPath))
- {
- MaxSdkLogger.UserError("Detected existing localization resource for \"" + localeCode + "\" locale. Skipping localization for User Tracking Usage Description. Please disable localization in AppLovin Integration manager and add the localizations to your existing resource.");
- return;
- }
- // Create intermediate directories as needed.
- if (!Directory.Exists(resourcesDirectoryPath))
- {
- Directory.CreateDirectory(resourcesDirectoryPath);
- }
- if (!Directory.Exists(localeSpecificDirectoryPath))
- {
- Directory.CreateDirectory(localeSpecificDirectoryPath);
- }
- var localizedDescriptionLine = "\"NSUserTrackingUsageDescription\" = \"" + localizedUserTrackingDescription + "\";\n";
- // File already exists, update it in case the value changed between builds.
- if (File.Exists(infoPlistStringsFilePath))
- {
- var output = new List<string>();
- var lines = File.ReadAllLines(infoPlistStringsFilePath);
- var keyUpdated = false;
- foreach (var line in lines)
- {
- if (line.Contains("NSUserTrackingUsageDescription"))
- {
- output.Add(localizedDescriptionLine);
- keyUpdated = true;
- }
- else
- {
- output.Add(line);
- }
- }
- if (!keyUpdated)
- {
- output.Add(localizedDescriptionLine);
- }
- File.WriteAllText(infoPlistStringsFilePath, string.Join("\n", output.ToArray()) + "\n");
- }
- // File doesn't exist, create one.
- else
- {
- File.WriteAllText(infoPlistStringsFilePath, "/* Localized versions of Info.plist keys - Generated by AL MAX plugin */\n" + localizedDescriptionLine);
- }
- var localeSpecificDirectoryRelativePath = Path.Combine(AppLovinMaxResourcesDirectoryName, localeSpecificDirectoryName);
- var guid = project.AddFolderReference(localeSpecificDirectoryRelativePath, localeSpecificDirectoryRelativePath);
- project.AddFileToBuild(targetGuid, guid);
- }
- private static bool ShouldRemoveLocalization(string localizedUserTrackingDescription)
- {
- if (string.IsNullOrEmpty(localizedUserTrackingDescription)) return true;
- var internalSettings = AppLovinInternalSettings.Instance;
- return !internalSettings.ConsentFlowEnabled || !internalSettings.UserTrackingUsageLocalizationEnabled;
- }
- private static void AddSwiftSupport(string buildPath, PBXProject project, string unityFrameworkTargetGuid, string unityMainTargetGuid)
- {
- var swiftFileRelativePath = "Classes/MAXSwiftSupport.swift";
- var swiftFilePath = Path.Combine(buildPath, swiftFileRelativePath);
- // Add Swift file
- CreateSwiftFile(swiftFilePath);
- var swiftFileGuid = project.AddFile(swiftFileRelativePath, swiftFileRelativePath);
- project.AddFileToBuild(unityFrameworkTargetGuid, swiftFileGuid);
- // Add Swift version property if needed
- var swiftVersion = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "SWIFT_VERSION");
- if (string.IsNullOrEmpty(swiftVersion))
- {
- project.SetBuildProperty(unityFrameworkTargetGuid, "SWIFT_VERSION", "5.0");
- }
- // Some publishers may configure these settings in their own post-processing scripts.
- // Only set them if they haven't already been defined to avoid overwriting publisher-defined values.
- var enableModules = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES");
- if (string.IsNullOrEmpty(enableModules))
- {
- project.SetBuildProperty(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES", "YES");
- }
- var alwaysEmbedSwiftLibraries = project.GetBuildPropertyForAnyConfig(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES");
- if (string.IsNullOrEmpty(alwaysEmbedSwiftLibraries))
- {
- project.SetBuildProperty(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
- }
- }
- private static void CreateSwiftFile(string swiftFilePath)
- {
- if (File.Exists(swiftFilePath)) return;
- // Create a file to write to.
- using (var writer = File.CreateText(swiftFilePath))
- {
- writer.WriteLine("//\n// MAXSwiftSupport.swift\n//");
- writer.WriteLine("\nimport Foundation\n");
- writer.WriteLine("// This file ensures the project includes Swift support.");
- writer.WriteLine("// It is automatically generated by the MAX Unity Plugin.");
- writer.Close();
- }
- }
- [PostProcessBuild(AppLovinPreProcess.CallbackOrder)]
- public static void MaxPostProcessPlist(BuildTarget buildTarget, string path)
- {
- var plistPath = Path.Combine(path, "Info.plist");
- var plist = new PlistDocument();
- plist.ReadFromFile(plistPath);
- RemoveAttributionReportEndpointIfNeeded(plist);
- EnableVerboseLoggingIfNeeded(plist);
- AddGoogleApplicationIdIfNeeded(plist);
- AddSdkSettings(plist, path);
- AddSkAdNetworksInfoIfNeeded(plist);
- RemoveSdkKeyIfNeeded(plist);
- plist.WriteToFile(plistPath);
- }
- private static void RemoveAttributionReportEndpointIfNeeded(PlistDocument plist)
- {
- PlistElement attributionReportEndPoint;
- plist.root.values.TryGetValue("NSAdvertisingAttributionReportEndpoint", out attributionReportEndPoint);
- // We no longer support this feature. Check if we had previously set the attribution endpoint and un-set it.
- if (attributionReportEndPoint == null || !AppLovinAdvertisingAttributionEndpoint.Equals(attributionReportEndPoint.AsString())) return;
- MaxSdkLogger.UserWarning("Global SKAdNetwork postback forwarding is no longer supported by AppLovin. Removing AppLovin Advertising Attribution Endpoint from Info.plist.");
- plist.root.values.Remove("NSAdvertisingAttributionReportEndpoint");
- }
- private static void EnableVerboseLoggingIfNeeded(PlistDocument plist)
- {
- if (!EditorPrefs.HasKey(MaxSdkLogger.KeyVerboseLoggingEnabled)) return;
- var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled);
- if (enabled)
- {
- plist.root.SetBoolean(AppLovinVerboseLoggingOnKey, true);
- }
- else
- {
- plist.root.values.Remove(AppLovinVerboseLoggingOnKey);
- }
- }
- private static void AddGoogleApplicationIdIfNeeded(PlistDocument plist)
- {
- if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
- const string googleApplicationIdentifier = "GADApplicationIdentifier";
- var appId = AppLovinSettings.Instance.AdMobIosAppId;
- // Log error if the App ID is not set.
- if (string.IsNullOrEmpty(appId) || !appId.StartsWith("ca-app-pub-"))
- {
- MaxSdkLogger.UserError("[AppLovin MAX] Google App ID is not set. Please enter a valid app ID within the AppLovin Integration Manager window.");
- return;
- }
- plist.root.SetString(googleApplicationIdentifier, appId);
- }
- private static void AddYandexSettingsIfNeeded(PBXProject project, string unityMainTargetGuid)
- {
- if (!AppLovinPackageManager.IsAdapterInstalled("Yandex")) return;
- if (MaxSdkUtils.CompareVersions(PlayerSettings.iOS.targetOSVersionString, "12.0") == MaxSdkUtils.VersionComparisonResult.Lesser)
- {
- MaxSdkLogger.UserWarning("Your iOS target version is under the minimum required version by Yandex. Please update it to 12.0 or newer in your ProjectSettings and rebuild your project.");
- return;
- }
- project.SetBuildProperty(unityMainTargetGuid, "GENERATE_INFOPLIST_FILE", "NO");
- }
- private static void AddSdkSettings(PlistDocument infoPlist, string buildPath)
- {
- var sdkSettingsPlistPath = Path.Combine(buildPath, AppLovinSettingsPlistFileName);
- var sdkSettingsPlist = new PlistDocument();
- if (File.Exists(sdkSettingsPlistPath))
- {
- sdkSettingsPlist.ReadFromFile(sdkSettingsPlistPath);
- }
- // Add the SDK key to the SDK settings plist.
- sdkSettingsPlist.root.SetString(KeySdkKey, AppLovinSettings.Instance.SdkKey);
- // Add consent flow settings if needed.
- EnableConsentFlowIfNeeded(sdkSettingsPlist, infoPlist);
- sdkSettingsPlist.WriteToFile(sdkSettingsPlistPath);
- var projectPath = PBXProject.GetPBXProjectPath(buildPath);
- var project = new PBXProject();
- project.ReadFromFile(projectPath);
- #if UNITY_2019_3_OR_NEWER
- var unityMainTargetGuid = project.GetUnityMainTargetGuid();
- #else
- var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
- #endif
- var guid = project.AddFile(AppLovinSettingsPlistFileName, AppLovinSettingsPlistFileName);
- project.AddFileToBuild(unityMainTargetGuid, guid);
- project.WriteToFile(projectPath);
- }
- private static void EnableConsentFlowIfNeeded(PlistDocument applovinSettingsPlist, PlistDocument infoPlist)
- {
- var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
- if (!consentFlowEnabled) return;
- var userTrackingUsageDescription = AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn;
- var privacyPolicyUrl = AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl;
- if (string.IsNullOrEmpty(userTrackingUsageDescription) || string.IsNullOrEmpty(privacyPolicyUrl))
- {
- AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL and the `User Tracking Usage Description` in the AppLovin Integration Manager. \n\n" +
- "Both values must be included to enable the SDK's consent flow.");
- // No need to update the info.plist here. Default consent flow state will be determined on the SDK side.
- return;
- }
- var consentFlowInfoRoot = applovinSettingsPlist.root.CreateDict(KeyConsentFlowInfo);
- consentFlowInfoRoot.SetBoolean(KeyConsentFlowEnabled, consentFlowEnabled);
- consentFlowInfoRoot.SetString(KeyConsentFlowPrivacyPolicy, privacyPolicyUrl);
- var termsOfServiceUrl = AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl;
- if (MaxSdkUtils.IsValidString(termsOfServiceUrl))
- {
- consentFlowInfoRoot.SetString(KeyConsentFlowTermsOfService, termsOfServiceUrl);
- }
- var shouldShowTermsAndPrivacyPolicyAlertInGdpr = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
- consentFlowInfoRoot.SetBoolean(KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR, shouldShowTermsAndPrivacyPolicyAlertInGdpr);
- var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
- if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
- {
- consentFlowInfoRoot.SetString(KeyConsentFlowDebugUserGeography, "gdpr");
- }
- infoPlist.root.SetString("NSUserTrackingUsageDescription", userTrackingUsageDescription);
- }
- private static void AddSkAdNetworksInfoIfNeeded(PlistDocument plist)
- {
- var skAdNetworkData = GetSkAdNetworkData();
- var skAdNetworkIds = skAdNetworkData.SkAdNetworkIds;
- // Check if we have a valid list of SKAdNetworkIds that need to be added.
- if (skAdNetworkIds == null || skAdNetworkIds.Length < 1) return;
- //
- // Add the SKAdNetworkItems to the plist. It should look like following:
- //
- // <key>SKAdNetworkItems</key>
- // <array>
- // <dict>
- // <key>SKAdNetworkIdentifier</key>
- // <string>ABC123XYZ.skadnetwork</string>
- // </dict>
- // <dict>
- // <key>SKAdNetworkIdentifier</key>
- // <string>123QWE456.skadnetwork</string>
- // </dict>
- // <dict>
- // <key>SKAdNetworkIdentifier</key>
- // <string>987XYZ123.skadnetwork</string>
- // </dict>
- // </array>
- //
- PlistElement skAdNetworkItems;
- plist.root.values.TryGetValue("SKAdNetworkItems", out skAdNetworkItems);
- var existingSkAdNetworkIds = new HashSet<string>();
- // Check if SKAdNetworkItems array is already in the Plist document and collect all the IDs that are already present.
- if (skAdNetworkItems != null && skAdNetworkItems.GetType() == typeof(PlistElementArray))
- {
- var plistElementDictionaries = skAdNetworkItems.AsArray().values.Where(plistElement => plistElement.GetType() == typeof(PlistElementDict));
- foreach (var plistElement in plistElementDictionaries)
- {
- PlistElement existingId;
- plistElement.AsDict().values.TryGetValue("SKAdNetworkIdentifier", out existingId);
- if (existingId == null || existingId.GetType() != typeof(PlistElementString) || string.IsNullOrEmpty(existingId.AsString())) continue;
- existingSkAdNetworkIds.Add(existingId.AsString());
- }
- }
- // Else, create an array of SKAdNetworkItems into which we will add our IDs.
- else
- {
- skAdNetworkItems = plist.root.CreateArray("SKAdNetworkItems");
- }
- foreach (var skAdNetworkId in skAdNetworkIds)
- {
- // Skip adding IDs that are already in the array.
- if (existingSkAdNetworkIds.Contains(skAdNetworkId)) continue;
- var skAdNetworkItemDict = skAdNetworkItems.AsArray().AddDict();
- skAdNetworkItemDict.SetString("SKAdNetworkIdentifier", skAdNetworkId);
- }
- }
- private static SkAdNetworkData GetSkAdNetworkData()
- {
- // Get the list of installed ad networks to be passed up
- var installedNetworks = AppLovinPackageManager.GetInstalledMediationNetworks();
- var uriBuilder = new UriBuilder("https://unity.applovin.com/max/1.0/skadnetwork_ids");
- var adNetworks = string.Join(",", installedNetworks.ToArray());
- if (MaxSdkUtils.IsValidString(adNetworks))
- {
- uriBuilder.Query += string.Format("ad_networks={0}", adNetworks);
- }
- using (var unityWebRequest = UnityWebRequest.Get(uriBuilder.ToString()))
- {
- var operation = unityWebRequest.SendWebRequest();
- // Wait for the download to complete or the request to timeout.
- while (!operation.isDone) { }
- #if UNITY_2020_1_OR_NEWER
- if (unityWebRequest.result != UnityWebRequest.Result.Success)
- #else
- if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
- #endif
- {
- MaxSdkLogger.UserError("Failed to retrieve SKAdNetwork IDs with error: " + unityWebRequest.error);
- return new SkAdNetworkData();
- }
- try
- {
- return JsonUtility.FromJson<SkAdNetworkData>(unityWebRequest.downloadHandler.text);
- }
- catch (Exception exception)
- {
- MaxSdkLogger.UserError("Failed to parse data '" + unityWebRequest.downloadHandler.text + "' with exception: " + exception);
- return new SkAdNetworkData();
- }
- }
- }
- private static void RemoveSdkKeyIfNeeded(PlistDocument plist)
- {
- if (!plist.root.values.ContainsKey(KeyAppLovinSdkKeyToRemove)) return;
- plist.root.values.Remove(KeyAppLovinSdkKeyToRemove);
- }
- }
- }
- #endif
|