|
|
|
|
//
|
|
|
|
|
// 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.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEditor.Callbacks;
|
|
|
|
|
#if UNITY_2019_3_OR_NEWER
|
|
|
|
|
using UnityEditor.iOS.Xcode.Extensions;
|
|
|
|
|
#endif
|
|
|
|
|
using UnityEditor.iOS.Xcode;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
|
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 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";
|
|
|
|
|
#else
|
|
|
|
|
private const string UnityMainTargetName = "Unity-iPhone";
|
|
|
|
|
#endif
|
|
|
|
|
private const string LegacyResourcesDirectoryName = "Resources";
|
|
|
|
|
private const string AppLovinMaxResourcesDirectoryName = "AppLovinMAXResources";
|
|
|
|
|
private const string AppLovinAdvertisingAttributionEndpoint = "https://postbacks-app.com";
|
|
|
|
|
|
|
|
|
|
private const string AppLovinSettingsPlistFileName = "AppLovin-Settings.plist";
|
|
|
|
|
private const string KeyConsentFlowInfo = "ConsentFlowInfo";
|
|
|
|
|
private const string KeyConsentFlowEnabled = "ConsentFlowEnabled";
|
|
|
|
|
private const string KeyConsentFlowTermsOfService = "ConsentFlowTermsOfService";
|
|
|
|
|
private const string KeyConsentFlowPrivacyPolicy = "ConsentFlowPrivacyPolicy";
|
|
|
|
|
private const string KeyConsentFlowDebugUserGeography = "ConsentFlowDebugUserGeography";
|
|
|
|
|
|
|
|
|
|
private static List<string> DynamicLibrariesToEmbed
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var dynamicLibrariesToEmbed = new List<string>
|
|
|
|
|
{
|
|
|
|
|
"AppLovinSDK.xcframework",
|
|
|
|
|
"DTBiOSSDK.xcframework",
|
|
|
|
|
"FBAEMKit.xcframework",
|
|
|
|
|
"FBSDKCoreKit_Basics.xcframework",
|
|
|
|
|
"FBSDKCoreKit.xcframework",
|
|
|
|
|
"FBSDKGamingServicesKit.xcframework",
|
|
|
|
|
"FBSDKLoginKit.xcframework",
|
|
|
|
|
"FBSDKShareKit.xcframework",
|
|
|
|
|
"HyprMX.xcframework",
|
|
|
|
|
"Maio.xcframework",
|
|
|
|
|
"MobileFuseSDK.xcframework",
|
|
|
|
|
"MolocoSDK.xcframework",
|
|
|
|
|
"OMSDK_Appodeal.xcframework",
|
|
|
|
|
"OMSDK_Ogury.xcframework",
|
|
|
|
|
"OMSDK_Pubnativenet.xcframework",
|
|
|
|
|
"OMSDK_Smaato.xcframework"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// LinkedIn Audience Network SDK is distributed as a static library starting version 1.2.0
|
|
|
|
|
if (AppLovinIntegrationManager.IsAdapterOlderThanMinVersionInstalled("LinkedIn", "1.2.0.0"))
|
|
|
|
|
{
|
|
|
|
|
dynamicLibrariesToEmbed.Add("LinkedinAudienceNetwork.xcframework");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fyber/IA SDK is distributed as a static library starting version 8.2.7
|
|
|
|
|
if (AppLovinIntegrationManager.IsAdapterOlderThanMinVersionInstalled("Fyber", "8.2.7.0"))
|
|
|
|
|
{
|
|
|
|
|
dynamicLibrariesToEmbed.Add("IASDKCore.xcframework");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AppLovinIntegrationManager.IsAdapterInstalled("InMobi", "10.7.2.0"))
|
|
|
|
|
{
|
|
|
|
|
dynamicLibrariesToEmbed.Add("InMobiSDK.xcframework");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AppLovinIntegrationManager.IsAdapterInstalled("Smaato", "22.8.3.0"))
|
|
|
|
|
{
|
|
|
|
|
dynamicLibrariesToEmbed.AddRange(new List<string>()
|
|
|
|
|
{
|
|
|
|
|
"SmaatoSDKBanner.xcframework",
|
|
|
|
|
"SmaatoSDKCore.xcframework",
|
|
|
|
|
"SmaatoSDKInAppBidding.xcframework",
|
|
|
|
|
"SmaatoSDKInterstitial.xcframework",
|
|
|
|
|
"SmaatoSDKNative.xcframework",
|
|
|
|
|
"SmaatoSDKOpenMeasurement.xcframework",
|
|
|
|
|
"SmaatoSDKOutstream.xcframework",
|
|
|
|
|
"SmaatoSDKRewardedAds.xcframework",
|
|
|
|
|
"SmaatoSDKRichMedia.xcframework",
|
|
|
|
|
"SmaatoSDKVideo.xcframework"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AppLovinIntegrationManager.IsAdapterInstalled("Verve", "3.0.0.0"))
|
|
|
|
|
{
|
|
|
|
|
dynamicLibrariesToEmbed.Add("ATOM.xcframework");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dynamicLibrariesToEmbed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string PluginMediationDirectory
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var pluginParentDir = AppLovinIntegrationManager.MediationSpecificPluginParentDirectory;
|
|
|
|
|
return Path.Combine(pluginParentDir, "MaxSdk/Mediation/");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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(int.MaxValue)] // 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[PostProcessBuildAttribute(int.MaxValue)]
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
var internalSettingsEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
|
|
|
|
|
var userTrackingUsageDescriptionDe = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe : AppLovinSettings.Instance.UserTrackingUsageDescriptionDe;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionDe, "de", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionEn = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn : AppLovinSettings.Instance.UserTrackingUsageDescriptionEn;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionEn, "en", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionEs = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs : AppLovinSettings.Instance.UserTrackingUsageDescriptionEs;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionEs, "es", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionFr = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr : AppLovinSettings.Instance.UserTrackingUsageDescriptionFr;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionFr, "fr", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionJa = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa : AppLovinSettings.Instance.UserTrackingUsageDescriptionJa;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionJa, "ja", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionKo = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo : AppLovinSettings.Instance.UserTrackingUsageDescriptionKo;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionKo, "ko", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionZhHans = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans : AppLovinSettings.Instance.UserTrackingUsageDescriptionZhHans;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionZhHans, "zh-Hans", buildPath, project, unityMainTargetGuid);
|
|
|
|
|
var userTrackingUsageDescriptionZhHant = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant : AppLovinSettings.Instance.UserTrackingUsageDescriptionZhHant;
|
|
|
|
|
LocalizeUserTrackingDescriptionIfNeeded(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)) return;
|
|
|
|
|
|
|
|
|
|
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 index = dynamicLibraryAbsolutePath.LastIndexOf("Pods");
|
|
|
|
|
var relativePath = dynamicLibraryAbsolutePath.Substring(index);
|
|
|
|
|
dynamicLibraryPathsPresentInProject.Add(relativePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dynamicLibraryPathsPresentInProject.Count <= 0) return;
|
|
|
|
|
|
|
|
|
|
#if UNITY_2019_3_OR_NEWER
|
|
|
|
|
if (ShouldEmbedDynamicLibraries(buildPath))
|
|
|
|
|
{
|
|
|
|
|
foreach (var dynamicLibraryPath in dynamicLibraryPathsPresentInProject)
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void LocalizeUserTrackingDescriptionIfNeeded(string localizedUserTrackingDescription, string localeCode, string buildPath, PBXProject project, string targetGuid)
|
|
|
|
|
{
|
|
|
|
|
// Use the legacy resources directory name if the build is being appended (the "Resources" directory already exists if it is an incremental build).
|
|
|
|
|
var resourcesDirectoryName = Directory.Exists(Path.Combine(buildPath, LegacyResourcesDirectoryName)) ? LegacyResourcesDirectoryName : AppLovinMaxResourcesDirectoryName;
|
|
|
|
|
var resourcesDirectoryPath = Path.Combine(buildPath, resourcesDirectoryName);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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(resourcesDirectoryName, localeSpecificDirectoryName);
|
|
|
|
|
var guid = project.AddFolderReference(localeSpecificDirectoryRelativePath, localeSpecificDirectoryRelativePath);
|
|
|
|
|
project.AddFileToBuild(targetGuid, guid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool ShouldRemoveLocalization(string localizedUserTrackingDescription)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(localizedUserTrackingDescription)) return true;
|
|
|
|
|
|
|
|
|
|
var settings = AppLovinSettings.Instance;
|
|
|
|
|
var internalSettings = AppLovinInternalSettings.Instance;
|
|
|
|
|
|
|
|
|
|
return (!internalSettings.ConsentFlowEnabled || !internalSettings.UserTrackingUsageLocalizationEnabled)
|
|
|
|
|
&& (!settings.ConsentFlowEnabled || !settings.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, PBXSourceTree.Source);
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enable Swift modules
|
|
|
|
|
project.AddBuildProperty(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES", "YES");
|
|
|
|
|
project.AddBuildProperty(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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[PostProcessBuildAttribute(int.MaxValue)]
|
|
|
|
|
public static void MaxPostProcessPlist(BuildTarget buildTarget, string path)
|
|
|
|
|
{
|
|
|
|
|
var plistPath = Path.Combine(path, "Info.plist");
|
|
|
|
|
var plist = new PlistDocument();
|
|
|
|
|
plist.ReadFromFile(plistPath);
|
|
|
|
|
|
|
|
|
|
SetSdkKeyIfNeeded(plist);
|
|
|
|
|
SetAttributionReportEndpointIfNeeded(plist);
|
|
|
|
|
|
|
|
|
|
EnableVerboseLoggingIfNeeded(plist);
|
|
|
|
|
AddGoogleApplicationIdIfNeeded(plist);
|
|
|
|
|
|
|
|
|
|
AddSdkSettingsIfNeeded(plist, path);
|
|
|
|
|
EnableTermsFlowIfNeeded(plist);
|
|
|
|
|
AddSkAdNetworksInfoIfNeeded(plist);
|
|
|
|
|
|
|
|
|
|
plist.WriteToFile(plistPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void SetSdkKeyIfNeeded(PlistDocument plist)
|
|
|
|
|
{
|
|
|
|
|
var sdkKey = AppLovinSettings.Instance.SdkKey;
|
|
|
|
|
if (string.IsNullOrEmpty(sdkKey)) return;
|
|
|
|
|
|
|
|
|
|
const string AppLovinVerboseLoggingOnKey = "AppLovinSdkKey";
|
|
|
|
|
plist.root.SetString(AppLovinVerboseLoggingOnKey, sdkKey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void SetAttributionReportEndpointIfNeeded(PlistDocument plist)
|
|
|
|
|
{
|
|
|
|
|
if (AppLovinSettings.Instance.SetAttributionReportEndpoint)
|
|
|
|
|
{
|
|
|
|
|
plist.root.SetString("NSAdvertisingAttributionReportEndpoint", AppLovinAdvertisingAttributionEndpoint);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PlistElement attributionReportEndPoint;
|
|
|
|
|
plist.root.values.TryGetValue("NSAdvertisingAttributionReportEndpoint", out attributionReportEndPoint);
|
|
|
|
|
|
|
|
|
|
// Check if we had previously set the attribution endpoint and un-set it.
|
|
|
|
|
if (attributionReportEndPoint != null && AppLovinAdvertisingAttributionEndpoint.Equals(attributionReportEndPoint.AsString()))
|
|
|
|
|
{
|
|
|
|
|
plist.root.values.Remove("NSAdvertisingAttributionReportEndpoint");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void EnableVerboseLoggingIfNeeded(PlistDocument plist)
|
|
|
|
|
{
|
|
|
|
|
if (!EditorPrefs.HasKey(MaxSdkLogger.KeyVerboseLoggingEnabled)) return;
|
|
|
|
|
|
|
|
|
|
var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled);
|
|
|
|
|
const string AppLovinVerboseLoggingOnKey = "AppLovinVerboseLoggingOn";
|
|
|
|
|
if (enabled)
|
|
|
|
|
{
|
|
|
|
|
plist.root.SetBoolean(AppLovinVerboseLoggingOnKey, enabled);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
plist.root.values.Remove(AppLovinVerboseLoggingOnKey);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void AddGoogleApplicationIdIfNeeded(PlistDocument plist)
|
|
|
|
|
{
|
|
|
|
|
if (!AppLovinIntegrationManager.IsAdapterInstalled("Google") && !AppLovinIntegrationManager.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-"))
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("[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 (!AppLovinIntegrationManager.IsAdapterInstalled("Yandex")) return;
|
|
|
|
|
|
|
|
|
|
if (MaxSdkUtils.CompareVersions(PlayerSettings.iOS.targetOSVersionString, "12.0") == MaxSdkUtils.VersionComparisonResult.Lesser)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogWarning("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 AddSdkSettingsIfNeeded(PlistDocument infoPlist, string buildPath)
|
|
|
|
|
{
|
|
|
|
|
// Right now internal settings is only needed for Consent Flow. Remove this setting once we add more settings.
|
|
|
|
|
if (!AppLovinInternalSettings.Instance.ConsentFlowEnabled) return;
|
|
|
|
|
|
|
|
|
|
var sdkSettingsPlistPath = Path.Combine(buildPath, AppLovinSettingsPlistFileName);
|
|
|
|
|
var sdkSettingsPlist = new PlistDocument();
|
|
|
|
|
if (File.Exists(sdkSettingsPlistPath))
|
|
|
|
|
{
|
|
|
|
|
sdkSettingsPlist.ReadFromFile(sdkSettingsPlistPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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, PBXSourceTree.Source);
|
|
|
|
|
project.AddFileToBuild(unityMainTargetGuid, guid);
|
|
|
|
|
project.WriteToFile(projectPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void EnableConsentFlowIfNeeded(PlistDocument applovinSettingsPlist, PlistDocument infoPlist)
|
|
|
|
|
{
|
|
|
|
|
var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
|
|
|
|
|
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 debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
|
|
|
|
|
if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
|
|
|
|
|
{
|
|
|
|
|
consentFlowInfoRoot.SetString(KeyConsentFlowDebugUserGeography, "gdpr");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infoPlist.root.SetString("NSUserTrackingUsageDescription", userTrackingUsageDescription);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void EnableTermsFlowIfNeeded(PlistDocument plist)
|
|
|
|
|
{
|
|
|
|
|
// Check if terms flow is enabled. No need to update info.plist if consent flow is disabled.
|
|
|
|
|
var consentFlowEnabled = AppLovinSettings.Instance.ConsentFlowEnabled;
|
|
|
|
|
if (!consentFlowEnabled) return;
|
|
|
|
|
|
|
|
|
|
// Check if terms flow is enabled for this format.
|
|
|
|
|
var consentFlowPlatform = AppLovinSettings.Instance.ConsentFlowPlatform;
|
|
|
|
|
if (consentFlowPlatform != Platform.All && consentFlowPlatform != Platform.iOS) return;
|
|
|
|
|
|
|
|
|
|
var userTrackingUsageDescription = AppLovinSettings.Instance.UserTrackingUsageDescriptionEn;
|
|
|
|
|
var privacyPolicyUrl = AppLovinSettings.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 = plist.root.CreateDict("AppLovinConsentFlowInfo");
|
|
|
|
|
consentFlowInfoRoot.SetBoolean("AppLovinConsentFlowEnabled", consentFlowEnabled);
|
|
|
|
|
consentFlowInfoRoot.SetString("AppLovinConsentFlowPrivacyPolicy", privacyPolicyUrl);
|
|
|
|
|
|
|
|
|
|
var termsOfServiceUrl = AppLovinSettings.Instance.ConsentFlowTermsOfServiceUrl;
|
|
|
|
|
if (!string.IsNullOrEmpty(termsOfServiceUrl))
|
|
|
|
|
{
|
|
|
|
|
consentFlowInfoRoot.SetString("AppLovinConsentFlowTermsOfService", termsOfServiceUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plist.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()
|
|
|
|
|
{
|
|
|
|
|
var uriBuilder = new UriBuilder("https://unity.applovin.com/max/1.0/skadnetwork_ids");
|
|
|
|
|
|
|
|
|
|
// Get the list of installed ad networks to be passed up
|
|
|
|
|
var maxMediationDirectory = PluginMediationDirectory;
|
|
|
|
|
if (Directory.Exists(maxMediationDirectory))
|
|
|
|
|
{
|
|
|
|
|
var mediationNetworkDirectories = Directory.GetDirectories(maxMediationDirectory);
|
|
|
|
|
var installedNetworks = mediationNetworkDirectories.Select(Path.GetFileName).ToList();
|
|
|
|
|
if (AppLovinSettings.Instance.AddApsSkAdNetworkIds)
|
|
|
|
|
{
|
|
|
|
|
installedNetworks.Add("AmazonPublisherServices");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var adNetworks = string.Join(",", installedNetworks.ToArray());
|
|
|
|
|
if (!string.IsNullOrEmpty(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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if UNITY_2019_3_OR_NEWER
|
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|