using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEditor; #if UNITY_IOS using UnityEditor.iOS.Xcode; #endif namespace Pack { public partial class PackPlatformiOS { protected override string GetBuildOutputPath() { // iOS 的XCode项目路径最好不能有中文,否则易出错 string path = Application.dataPath + PackConstant.BuildAppPath + "/" + GetBuildTarget() + "/" + channelUniqueId; path = Path.GetFullPath(path).Replace('\\', '/'); return path + "/XCodeProj"; } public override void BuildClear() { base.BuildClear(); DeleteDirectoryAssets(Path.GetFullPath(Application.dataPath + PackConstant.PluginiOSDestPath), true); DeleteDirectoryAssets(Path.GetFullPath(Application.dataPath + PackConstant.SDKBridgeDestPath), true); } protected override void ChangePlayerSettings() { base.ChangePlayerSettings(); PlayerSettings.bundleVersion = gameVersionCode.ToString(true); PlayerSettings.iOS.buildNumber = gameVersionCode; } protected override void ChangePlatformSpecial() { base.ChangePlatformSpecial(); EditorUtility.DisplayProgressBar("平台特殊设置", "开始平台特殊设置", 0); if (plugins != null && plugins.Count > 0) { CopyUnityAssets(Application.dataPath + PackConstant.PluginiOSSrcPath , plugins , Application.dataPath + PackConstant.PluginiOSDestPath , Application.dataPath + PackConstant.PluginiOSSrcMetaPath , Application.dataPath + PackConstant.PluginiOSDestMetaPath); } EditorUtility.DisplayProgressBar("平台特殊设置", "平台特殊设置", 0.5f); if (sdkBridges != null && sdkBridges.Count > 0) { CopyUnityAssets(Application.dataPath + PackConstant.SDKBridgeSrcPath , sdkBridges , Application.dataPath + PackConstant.SDKBridgeDestPath , Application.dataPath + PackConstant.SDKBridgeSrcMetaPath , Application.dataPath + PackConstant.SDKBridgeDestMetaPath); } EditorUtility.DisplayProgressBar("平台特殊设置", "完成平台特殊设置", 1); } protected override BuildOptions ChangeBuildOptionsBefore(BuildOptions buildOptions) { buildOptions = base.ChangeBuildOptionsBefore(buildOptions); return buildOptions; } private void CopymmFile(string[] fs,string topath) { string path = Application.dataPath + PackConstant.BuildSharedResourcePath + "/Plugins/Special/iOS/"; string outPath = GetBuildOutputPath(); foreach (var item in fs) { File.Copy(path + item, Path.Combine(outPath,topath+ "/"+item), true); } } protected override void BuildAppCompleted(BuildOptions buildOptions) { base.BuildAppCompleted(buildOptions); string outPath = GetBuildOutputPath(); // iOS 的XCode项目路径最好不能有中文,否则易出错 // 所以设置一个txt文件来标注项目名字 string dirPath = Path.GetDirectoryName(outPath); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } string[] files = Directory.GetFiles(dirPath, ".txt"); bool exist = false; string packFileName = GetPackFileName(); foreach (string file in files) { if (Path.GetFileNameWithoutExtension(file) == packFileName) { exist = true; } else { File.Delete(file); } } if (!exist) { //packFileName = packFileName.Replace(" ", "_"); packFileName = packFileName.Replace(":", "_"); File.Create(Path.Combine(dirPath, packFileName + ".txt")); } // 为了过审核,需要删除OpenUrl //File.Copy(Application.dataPath + PackConstant.BuildSharedResourcePath + "/Plugins/Special/iOS/libiPhone-lib.a", Path.Combine(outPath, "Libraries/libiPhone-lib.a"), true); //CopymmFile(new string[] { "LuLuConnector.h", "LuLuConnector.mm" }, "Classes"); ChangeXCodeProject(buildOptions); } #if UNITY_IOS private void AddFrameworkToProject(PBXProject pro, string target, string[] libs, bool weak) { foreach (var item in libs) { pro.AddFrameworkToProject(target,item,weak); } } private PlistElementArray PlistAddUrlType(PlistDocument plist, PlistElementArray urlTypes,string name,string role, string url) { if (urlTypes == null) { if (plist.root.values.ContainsKey("CFBundleURLTypes")) { urlTypes = plist.root["CFBundleURLTypes"].AsArray(); } else urlTypes = plist.root.CreateArray("CFBundleURLTypes"); } PlistElementDict dict = urlTypes.AddDict(); dict.SetString("CFBundleTypeRole", role); dict.SetString("CFBundleURLName", name); dict.CreateArray("CFBundleURLSchemes").AddString(url); return urlTypes; } private void PlistCreateArray(PlistDocument plist, string name,string[] values) { PlistElementArray arr = plist.root.CreateArray(name); foreach (var item in values) { arr.AddString(item); } } class DictItem { public string type; public object data; public DictItem(string type,object data) { this.data = data; this.type = type; } } private PlistElementDict PlistCreateDict(PlistDocument plist,string name, Dictionary keyValues,bool isRoot = false, PlistElementDict root = null) { PlistElementDict dict = plist.root; if (root == null) { root = plist.root; } if (!isRoot) dict = root.CreateDict(name); foreach (var item in keyValues) { switch (item.Value.type) { case "bool": { dict.SetBoolean(item.Key,(bool)item.Value.data); } break; case "int": { dict.SetInteger(item.Key,(int)item.Value.data); } break; case "float": { dict.SetReal(item.Key,(float)item.Value.data); } break; case "date": { dict.SetDate(item.Key,(System.DateTime)item.Value.data); } break; case "string": { dict.SetString(item.Key,(string)item.Value.data); } break; } } return dict; } #endif protected void ChangeXCodeProject(BuildOptions buildOptions) { #if UNITY_IOS EditorUtility.DisplayProgressBar("修改XCodeProject", "开始修改XCodeProject", 0); string outPath = GetBuildOutputPath(); PBXProject pbxProject = new PBXProject(); string pbxprojPath = PBXProject.GetPBXProjectPath(outPath); pbxProject.ReadFromFile(pbxprojPath); string targetGuid = pbxProject.GetUnityMainTargetGuid();//pbxProject.TargetGuidByName("Unity-iPhone"); string unityFmwkGUid = pbxProject.GetUnityFrameworkTargetGuid(); Debug.Log($"=================== targetGuid = {targetGuid} ==============="); //pbxProject.AddCopyFilesBuildPhase(targetGuid, "SSBundle.bundle", Application.dataPath + "/Plugins/iOS", ""); pbxProject.SetBuildProperty(targetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym"); pbxProject.SetBuildProperty(targetGuid, "GCC_GENERATE_DEBUGGING_SYMBOLS", "YES"); pbxProject.SetBuildProperty(targetGuid, "COPY_PHASE_STRIP", "NO"); pbxProject.SetBuildProperty(targetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES"); pbxProject.SetBuildProperty(targetGuid, "GCC_ENABLE_CPP_EXCEPTIONS", "YES"); pbxProject.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO"); pbxProject.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC -lz"); pbxProject.AddBuildProperty(unityFmwkGUid, "OTHER_LDFLAGS", "-ObjC"); pbxProject.SetBuildProperty(targetGuid, "SWIFT_VERSION", "5.0"); //pbxProject.AddBuildProperty(targetGuid, "Library Search Path", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"); //pbxProject.AddBuildProperty(targetGuid, "Library Search Path", "$(SDKROOT)/usr/lib/swift"); pbxProject.AddBuildProperty(unityFmwkGUid, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)"); //pbxProject.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SDKROOT)/usr/lib/swift"); pbxProject.AddBuildProperty(unityFmwkGUid, "LIBRARY_SEARCH_PATHS", "$(SDKROOT)/usr/lib/swift"); pbxProject.AddBuildProperty(targetGuid, "Runpath Search Path", "/usr/lib/swift"); //pbxProject.AddBuildProperty(targetGuid, "RUNPATH_SEARCH_PATHS", "/usr/lib/swift"); //AdSupport.framework AuthenticationServices.framework, Accelerate.framework pbxProject.AddCapability(targetGuid, PBXCapabilityType.InAppPurchase);//内购 pbxProject.AddCapability(targetGuid, PBXCapabilityType.SignInWithApple);//苹果登录 AddFrameworkToProject(pbxProject, targetGuid, new string[] { "libz.dylib", "libc++.dylib", "AdSupport.framework", "AuthenticationServices.framework", "Accelerate.framework" }, false); AddFrameworkToProject(pbxProject, unityFmwkGUid, new string[] { "Accelerate.framework" }, false); pbxProject.WriteToFile(pbxprojPath); EditorUtility.DisplayProgressBar("修改XCodeProject", "修改project.pbxproj完成", 0.25f); string plistPath = Path.Combine(outPath, "Info.plist"); PlistDocument plistDocument = new PlistDocument(); plistDocument.ReadFromFile(plistPath); //plistDocument.root.SetString("FacebookUrlSchemeSuffix", "3db62ef3789f3752a4a6d1b7ffc6f922"); /* FacebookAppID:facebook 后台应用参数 * FacebookUrlSchemeSuffix 应用参数后缀,后缀仅可使用字母字符。(可不设置,当多应用共用一个 appid 时需添加,可避免 FB 登录成功后返回到其他应用) * FacebookDisplayNam:facebook 后台配置的应用名称{游戏名称}; * FacebookClientToken:在 facebook 后台应用设置->高级->客户端口令查看; * LSApplicationQueriesSchemes:Facebook 相关白名单。*/ //QkSDK_GameId plistDocument.root.SetString("QkSDK_GameId", "38269516260030609925583594255948"); plistDocument.root.SetString("FacebookAppID", "743858834901692"); plistDocument.root.SetString("FacebookDisplayNam", "萌新冒險家"); plistDocument.root.SetString("FacebookClientToken", "0b39acbe80e234d4b9691451af1a4412"); PlistElementArray plArr = plistDocument.root.CreateArray("LSApplicationQueriesSchemes"); //fbapi //fb-messenger-api //fbauth2 //fbshareextension //fb-messenger-share-api plArr.AddString("fbapi"); plArr.AddString("fb-messenger-api"); plArr.AddString("fbauth2"); plArr.AddString("fbshareextension"); plArr.AddString("fb-messenger-share-api"); PlistAddUrlType(plistDocument,null,"fb","Editor", "fb743858834901692"); plistDocument.WriteToFile(plistPath); EditorUtility.DisplayProgressBar("修改XCodeProject", "修改info.list完成", 0.5f); if ((buildOptions & BuildOptions.AcceptExternalModificationsToPlayer) == 0) { string deviceSettingsMMPath = Path.Combine(outPath, "Classes/Unity/DeviceSettings.mm"); ObjCScript script = new ObjCScript(deviceSettingsMMPath); script.AddImport("WentingSDK.h"); script.ReplaceInLine("_VendorID = AllocCString([[UIDevice currentDevice].identifierForVendor UUIDString]);", "_VendorID = AllocCString([WentingSDK GetUUID]);"); script.Save(); /* string unityAppControllerMMPath = Path.Combine(outPath, "Classes/UnityAppController.mm"); script = new ObjCScript(unityAppControllerMMPath); //script.AddImport("OEGFramework", true, true); int idx = script.FindLine("::printf(\"-> applicationDidFinishLaunching()\\n\");"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "[OEGManager handleDidFinishLaunchingWithOptions:launchOptions];"); script.InsertLine(idx, idx + 3, "// If you want to control Firebase push message you can add below code"); script.InsertLine(idx, idx + 4, "[[FirebaseService sharedManager] messagingDelegate:self];"); script.InsertLine(idx, idx + 5); idx = script.FindLine("::printf(\"-> applicationWillResignActive()\\n\");"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "[OEGManager handleWillResignActive];"); script.InsertLine(idx, idx + 3); idx = script.FindLine("::printf(\"-> applicationDidEnterBackground()\\n\");"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, " [OEGManager handleDidEnterBackground];"); script.InsertLine(idx, idx + 3); idx = script.FindLine("::printf(\"-> applicationWillEnterForeground()\\n\");"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "[OEGManager handleWillEnterForeground];"); script.InsertLine(idx, idx + 3); idx = script.FindLine("::printf(\"-> applicationDidBecomeActive()\\n\");"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "[OEGManager handleDidBecomeActive];"); script.InsertLine(idx, idx + 3); idx = script.FindLine("::printf(\"-> applicationWillTerminate()\\n\");"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "[OEGManager handleWillTerminate];"); script.InsertLine(idx, idx + 3); idx = script.FindLine("@end") - 1; //script.InsertLine(idx, idx + 1); //script.InsertLine(idx, idx + 2, "- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {"); //script.InsertLine(idx, idx + 3, " [OEGManager handleDidRegisterForRemoteNotificationsWithDeviceToken:deviceToken];\n}"); script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {"); script.InsertLine(idx, idx + 3, " return [OEGManager handleOpenURL:url options:options];\n}"); script.InsertLine(idx, idx + 4); script.InsertLine(idx, idx + 5); // idx = script.FindLine("- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken") + 1; script.InsertLine(idx, idx + 1); script.InsertLine(idx, idx + 2, "[OEGManager handleDidRegisterForRemoteNotificationsWithDeviceToken:deviceToken];"); script.InsertLine(idx, idx + 3); script.Save(); */ } EditorUtility.DisplayProgressBar("修改XCodeProject", "完成修改XCodeProject", 1); EditorUtility.ClearProgressBar(); #endif } protected class ObjCScript { private string m_FilePath; private List m_Lines; private int importIdx = -1; private int includeIdx = -1; public ObjCScript(string filePath) { m_FilePath = filePath; if (!File.Exists(filePath)) { PackLog.LogError(filePath + " is not Exist"); return; } m_Lines = new List(File.ReadAllLines(filePath)); for (int i = 0, iMax = m_Lines.Count; i < iMax; i++) { string line = m_Lines[i].Trim(); if (line.StartsWith("#import ")) { importIdx = i; } if (line.StartsWith("#include ")) { includeIdx = i; } } } public void AddImport(string name, bool isframework = false,bool ismode = false) { if (m_Lines == null) { PackLog.LogError(m_FilePath + " is not Valid Objc Script"); return; } int idx = -1; if (importIdx >= 0 && importIdx < m_Lines.Count) { idx = importIdx; } if (idx == -1) { if (includeIdx >= 0 && includeIdx < m_Lines.Count) { idx = includeIdx; } } if (idx == -1) { PackLog.LogError(m_FilePath + " is not Valid Objc Script"); return; } if (isframework) { idx = idx + 1; if (ismode) { m_Lines.Insert(idx, "@import " + name + ";"); } else { m_Lines.Insert(idx, "#import <" + name + ">"); } } else { idx = idx + 1; m_Lines.Insert(idx, "#import \"" + name + "\""); } } public int FindLine(string str) { if (m_Lines == null) { PackLog.LogError(m_FilePath + " is not Valid Objc Script"); return -1; } return m_Lines.FindIndex((x) => x.Contains(str)); } public void InsertLine(int alginIdx, int insertIdx, string str = "") { if (m_Lines == null) { PackLog.LogError(m_FilePath + " is not Valid Objc Script"); return; } if (alginIdx < 0 || alginIdx >= m_Lines.Count) { PackLog.LogError(" alginIdx is not Valid Value " + alginIdx); return; } if (insertIdx < 0 || insertIdx >= m_Lines.Count) { PackLog.LogError(" insertIdx is not Valid Value " + insertIdx); return; } if (string.IsNullOrEmpty(str)) { m_Lines.Insert(insertIdx, ""); return; } string line = m_Lines[alginIdx]; int len1 = line.Length; int len2 = len1 - line.TrimStart().Length; string startStr = line.Substring(0, len2); m_Lines.Insert(insertIdx, startStr + str); return; } public void ReplaceInLine(string oldStr, string newStr) { if (m_Lines == null) { PackLog.LogError(m_FilePath + " is not Valid Objc Script"); return; } for (int i = 0, iMax = m_Lines.Count; i < iMax; i++) { string line = m_Lines[i]; if (line.Contains(oldStr)) { m_Lines[i] = line.Replace(oldStr, newStr); } } } public void Save() { if (m_Lines == null) return; File.WriteAllLines(m_FilePath, m_Lines); } public override string ToString() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int i = 0, iMax = m_Lines.Count; i < iMax; i++) { sb.AppendLine(m_Lines[i]); } return sb.ToString(); } } } }