三国卡牌客户端基础资源仓库
hch
2026-06-15 b2ceae0fd6d520fd068de9ddc5d1e9c11da3983d
0312 同步文档
1个文件已修改
285 ■■■■■ 已修改文件
Assets/Editor/XCodeProjectMod.cs 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/XCodeProjectMod.cs
@@ -46,8 +46,16 @@
        project.SetBuildProperty(targetGUID, "ENABLE_BITCODE", "NO");
        project.SetBuildProperty(targetGUID, "DEVELOPMENT_TEAM", TEAM);
        // 1.1 环境要求:iOS 13.0 及以上
        project.SetBuildProperty(targetGUID, "IPHONEOS_DEPLOYMENT_TARGET", "13.0");
        project.SetBuildProperty(fwTargetGUID, "IPHONEOS_DEPLOYMENT_TARGET", "13.0");
        project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-ObjC");
        project.AddBuildProperty(targetGUID, "GCC_GENERATE_DEBUGGING_SYMBOLS", "NO");
        // Firebase 调试日志参数
        project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-FIRAnalyticsDebugEnabled");
        project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-FIRDebugEnabled");
        project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-FIRAnalyticsVerboseLoggingEnabled");
        //wkwebview
        project.AddFrameworkToProject(fwTargetGUID, "WebKit.framework", true);
@@ -92,7 +100,162 @@
            }
        }
        // Adjust SDK 依赖的系统框架(文档要求 Status 设为 Optional)
        project.AddFrameworkToProject(fwTargetGUID, "AdServices.framework", true);
        project.AddFrameworkToProject(fwTargetGUID, "AdSupport.framework", true);
        project.AddFrameworkToProject(fwTargetGUID, "AppTrackingTransparency.framework", true);
        project.AddFrameworkToProject(fwTargetGUID, "StoreKit.framework", true);
        File.WriteAllText(_projectPath, project.WriteToString());
        // SPM 依赖:Firebase / Facebook / Adjust(Unity PBXProject API 不支持 SPM,需直接操作 pbxproj)
        AddSwiftPackageDependencies(_projectPath, targetGUID);
    }
    /// <summary>
    /// 向 pbxproj 注入 Swift Package Manager 依赖(XCRemoteSwiftPackageReference + XCSwiftPackageProductDependency)
    /// 版本号需根据 demo 或实际需求调整
    /// </summary>
    private static void AddSwiftPackageDependencies(string projectPath, string targetGUID)
    {
        string content = File.ReadAllText(projectPath);
        // ---- 1. 生成 GUID(使用确定性命名避免重复注入)----
        string firebasePkgRefGuid  = "FIMPSPM00001"; // XCRemoteSwiftPackageReference
        string fbPkgRefGuid        = "FIMPSPM00002";
        string adjustPkgRefGuid    = "FIMPSPM00003";
        string firebaseAnalyticsProdGuid = "FIMPSPM00011"; // XCSwiftPackageProductDependency (FirebaseAnalytics)
        string firebaseCoreProdGuid      = "FIMPSPM00012"; // XCSwiftPackageProductDependency (FirebaseCore)
        string fbCoreKitProdGuid         = "FIMPSPM00021"; // XCSwiftPackageProductDependency (FacebookCore)
        string fbLoginKitProdGuid        = "FIMPSPM00022"; // XCSwiftPackageProductDependency (FacebookLogin)
        string adjustProdGuid            = "FIMPSPM00031"; // XCSwiftPackageProductDependency (Adjust)
        // 如果已经注入过,跳过
        if (content.Contains(firebasePkgRefGuid))
        {
            UnityEngine.Debug.Log("[XCodeProjectMod] SPM dependencies already injected, skipping.");
            return;
        }
        // ---- 2. XCRemoteSwiftPackageReference section ----
        string pkgRefSection = "/* XCRemoteSwiftPackageReference section */";
        string pkgRefEntries = string.Format(
            "\t\t{{0}} /* XCRemoteSwiftPackageReference \"Firebase\" */ = {{isa = XCRemoteSwiftPackageReference; repositoryURL = \"https://github.com/firebase/firebase-ios-sdk\"; requirement = {{kind = upToNextMajorVersion; minimumVersion = 10.24.0; }}; }};\n" +
            "\t\t{{1}} /* XCRemoteSwiftPackageReference \"Facebook\" */ = {{isa = XCRemoteSwiftPackageReference; repositoryURL = \"https://github.com/facebook/facebook-ios-sdk\"; requirement = {{kind = upToNextMajorVersion; minimumVersion = 17.0.0; }}; }};\n" +
            "\t\t{{2}} /* XCRemoteSwiftPackageReference \"Adjust\" */ = {{isa = XCRemoteSwiftPackageReference; repositoryURL = \"https://github.com/adjust/ios_sdk\"; requirement = {{kind = upToNextMajorVersion; minimumVersion = 4.38.2; }}; }};\n",
            firebasePkgRefGuid, fbPkgRefGuid, adjustPkgRefGuid);
        content = content.Replace(pkgRefSection, pkgRefSection + "\n" + pkgRefEntries);
        // ---- 3. XCSwiftPackageProductDependency section ----
        string pkgProdSection = "/* XCSwiftPackageProductDependency section */";
        string pkgProdEntries = string.Format(
            "\t\t{{0}} /* FirebaseAnalytics */ = {{isa = XCSwiftPackageProductDependency; package = {{1}} /* XCRemoteSwiftPackageReference \"Firebase\" */; productName = FirebaseAnalytics; }};\n" +
            "\t\t{{2}} /* FirebaseCore */ = {{isa = XCSwiftPackageProductDependency; package = {{1}} /* XCRemoteSwiftPackageReference \"Firebase\" */; productName = FirebaseCore; }};\n" +
            "\t\t{{3}} /* FacebookCore */ = {{isa = XCSwiftPackageProductDependency; package = {{4}} /* XCRemoteSwiftPackageReference \"Facebook\" */; productName = FacebookCore; }};\n" +
            "\t\t{{5}} /* FacebookLogin */ = {{isa = XCSwiftPackageProductDependency; package = {{4}} /* XCRemoteSwiftPackageReference \"Facebook\" */; productName = FacebookLogin; }};\n" +
            "\t\t{{6}} /* Adjust */ = {{isa = XCSwiftPackageProductDependency; package = {{7}} /* XCRemoteSwiftPackageReference \"Adjust\" */; productName = Adjust; }};\n",
            firebaseAnalyticsProdGuid, firebasePkgRefGuid,
            firebaseCoreProdGuid,
            fbCoreKitProdGuid, fbPkgRefGuid,
            fbLoginKitProdGuid,
            adjustProdGuid, adjustPkgRefGuid);
        content = content.Replace(pkgProdSection, pkgProdSection + "\n" + pkgProdEntries);
        // ---- 4. PBXNativeTarget packageProductDependencies ----
        // 在 target 的 packageProductDependencies 中添加引用
        string pkgProdDeps = string.Format(
            "\t\t\t\tpackageProductDependencies = (\n" +
            "\t\t\t\t\t{0} /* FirebaseAnalytics */,\n" +
            "\t\t\t\t\t{1} /* FirebaseCore */,\n" +
            "\t\t\t\t\t{2} /* FacebookCore */,\n" +
            "\t\t\t\t\t{3} /* FacebookLogin */,\n" +
            "\t\t\t\t\t{4} /* Adjust */,\n" +
            "\t\t\t\t);",
            firebaseAnalyticsProdGuid, firebaseCoreProdGuid,
            fbCoreKitProdGuid, fbLoginKitProdGuid,
            adjustProdGuid);
        // 找到 main target 的 buildPhases 结束位置附近,插入 packageProductDependencies
        // 在 target 的 buildPhases 数组结束后添加
        string targetMarker = targetGUID + " /* " + PBXProject.GetUnityTargetName() + " */";
        int targetStart = content.IndexOf(targetMarker);
        if (targetStart >= 0)
        {
            // 找到这个 target dict 中的 buildPhases 结束的 );
            int buildPhasesEnd = FindClosingParen(content, targetStart, "buildPhases");
            if (buildPhasesEnd >= 0)
            {
                content = content.Insert(buildPhasesEnd, "\n" + pkgProdDeps);
            }
        }
        // ---- 5. PBXProject packageReferences ----
        // 在 project 的 packageReferences 中添加引用
        string pkgRefs = string.Format(
            "\t\t\t\tpackageReferences = (\n" +
            "\t\t\t\t\t{{0}} /* XCRemoteSwiftPackageReference \"Firebase\" */,\n" +
            "\t\t\t\t\t{{1}} /* XCRemoteSwiftPackageReference \"Facebook\" */,\n" +
            "\t\t\t\t\t{{2}} /* XCRemoteSwiftPackageReference \"Adjust\" */,\n" +
            "\t\t\t\t);",
            firebasePkgRefGuid, fbPkgRefGuid, adjustPkgRefGuid);
        // 在 PBXProject 的 mainGroup 后面添加 packageReferences
        int projectSection = content.IndexOf("/* PBXProject */ = {");
        if (projectSection >= 0)
        {
            // 找到 attributes 区块
            int attrsPos = content.IndexOf("attributes = {", projectSection);
            if (attrsPos >= 0)
            {
                // 在 attributes 闭括号前插入
                int attrsClose = content.IndexOf("};", attrsPos + 14);
                if (attrsClose >= 0)
                {
                    content = content.Insert(attrsClose, "\n" + pkgRefs);
                }
            }
        }
        // ---- 6. PBXFrameworksBuildPhase ----
        // 将 SPM product 加到 Frameworks build phase
        string frameworkBuildPhaseMarker = "/* Frameworks */ = {";
        int fwPhaseIdx = content.IndexOf(frameworkBuildPhaseMarker);
        if (fwPhaseIdx >= 0)
        {
            int filesStart = content.IndexOf("files = (", fwPhaseIdx);
            if (filesStart >= 0)
            {
                int filesEnd = content.IndexOf(");", filesStart + 9);
                if (filesEnd >= 0)
                {
                    string fwEntries = string.Format(
                        "\t\t\t\t{0} /* FirebaseAnalytics in Frameworks */,\n" +
                        "\t\t\t\t{1} /* FirebaseCore in Frameworks */,\n" +
                        "\t\t\t\t{2} /* FacebookCore in Frameworks */,\n" +
                        "\t\t\t\t{3} /* FacebookLogin in Frameworks */,\n" +
                        "\t\t\t\t{4} /* Adjust in Frameworks */,\n",
                        firebaseAnalyticsProdGuid, firebaseCoreProdGuid,
                        fbCoreKitProdGuid, fbLoginKitProdGuid,
                        adjustProdGuid);
                    content = content.Insert(filesEnd, fwEntries);
                }
            }
        }
        File.WriteAllText(projectPath, content);
        UnityEngine.Debug.Log("[XCodeProjectMod] SPM dependencies (Firebase/Facebook/Adjust) injected successfully.");
    }
    /// <summary>
    /// 在 pbxproj 文本中,从指定 key(如 buildPhases)的数组起始位置找到其闭括号 ");" 的位置
    /// </summary>
    private static int FindClosingParen(string content, int searchStart, string key)
    {
        int keyIdx = content.IndexOf(key + " = (", searchStart);
        if (keyIdx < 0) return -1;
        int openIdx = content.IndexOf("(", keyIdx);
        return content.IndexOf(");", openIdx);
    }
    private static void BuildPlist(string path)
@@ -103,23 +266,22 @@
        _plist.ReadFromString(File.ReadAllText(_plistPath));
        PlistElementDict _rootDict = _plist.root;
        PlistElementDict _nsAppTransportSecurity = _rootDict.CreateDict("NSAppTransportSecurity");
        _nsAppTransportSecurity.SetBoolean("NSAllowsArbitraryLoads", true);
        _rootDict.SetString("NSPhotoLibraryAddUsageDescription", "You must grant the photo album permission to take screenshots and save data for users");
        _rootDict.SetString("NSCameraUsageDescription", "Please allow application permission access, otherwise the function cannot be started");
        _rootDict.SetString("NSUserTrackingUsageDescription", "This guide will display personalized ads specifically for you");
        // Adjust (funshine.plist: kAdjustAppToken)
        _rootDict.SetString("AdjustAppToken", "nbevz727gy68");
        _rootDict.SetString("NSUserTrackingUsageDescription", "Please rest assured, granting permission will not access your private information on other websites. This permission is only used to identify the device and ensure service security and enhance browsing experience");
        // Facebook (funshine.plist: kFacebookAppID / kFacebookAppSecret)
        _rootDict.SetString("FacebookAppID", "1445747540428317");
        _rootDict.SetString("FacebookClientToken", "");
        _rootDict.SetString("FacebookDisplayName", "Legend of the Sword Master");
        _rootDict.SetBoolean("FacebookAutoLogAppEventsEnabled", true);
        _rootDict.SetBoolean("FacebookAdvertiserIDCollectionEnabled", true);
        // 适配欧盟地区政策法规 - Google Analytics 默认允许
        _rootDict.SetBoolean("GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE", true);
        _rootDict.SetBoolean("GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE", true);
        _rootDict.SetBoolean("GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA", true);
        _rootDict.SetBoolean("GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS", true);
        // App 名称本地化
        _rootDict.SetBoolean("Application has localized display name", true);
        _rootDict.SetString("CFBundleDevelopmentRegion", "english");
        PlistElementArray nsURLTypes = _rootDict.CreateArray("CFBundleURLTypes");
        var dict0 = nsURLTypes.AddDict();
@@ -130,18 +292,7 @@
        var array1 = _rootDict.CreateArray("LSApplicationQueriesSchemes");
        array1.AddString("fbapi");
        array1.AddString("fbapi20130214");
        array1.AddString("fbapi20130410");
        array1.AddString("fbapi20130702");
        array1.AddString("fbapi20131010");
        array1.AddString("fbapi20131219");
        array1.AddString("fbapi20140410");
        array1.AddString("fbapi20140116");
        array1.AddString("fbapi20150313");
        array1.AddString("fbapi20150629");
        array1.AddString("fbapi20160328");
        array1.AddString("fbauth");
        array1.AddString("fb-messenger-share-api");
        array1.AddString("fb-messenger-api");
        array1.AddString("fbauth2");
        array1.AddString("fbshareextension");
@@ -170,6 +321,7 @@
        // fimpfunSDK 生命周期
        _xclass.WriteBelow("- (BOOL)application:(UIApplication*)app openURL:(NSURL*)url options:(NSDictionary<NSString*, id>*)options\n{", "\n    [_fimpfunBridge application:app openURL:url options:options];");
        _xclass.WriteBelow("- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation\n{", "\n    return [_fimpfunBridge application:application openURL:url sourceApplication:sourceApplication annotation:annotation];");
        _xclass.WriteBelow("::printf(\"-> applicationDidBecomeActive()\\n\");", "[_fimpfunBridge applicationDidBecomeActive:application];");
@@ -179,6 +331,91 @@
        _xclass.WriteBelow("DisplayConnection*  _mainDisplay;", "FimpfunBridge* _fimpfunBridge;");
        _xclass.WriteBelow("@property (readonly, copy, nonatomic) DisplayConnection*    mainDisplay;",
                           "@property (readonly, copy, nonatomic) FimpfunBridge*  fimpfunBridge;");
        // 3.1 适配欧盟政策:在 viewDidAppear 中调用 adaptingEUGDPR(文档要求)
        var _viewController = new XClass(projectPath + "/Classes/UnityViewControllerBase.mm");
        _viewController.WriteBelow("@implementation UnityViewControllerBase",
            "\n- (void)viewDidAppear:(BOOL)animated {\n    [super viewDidAppear:animated];\n    [GetAppController().fimpfunBridge viewDidAppear];\n}\n");
        // 创建 InfoPlist.strings(App 名称本地化)
        CreateInfoPlistStrings(projectPath);
    }
    /// <summary>
    /// 创建 InfoPlist.strings 文件用于 App 名称本地化
    /// </summary>
    private static void CreateInfoPlistStrings(string projectPath)
    {
        string stringsDir = projectPath + "/English.lproj";
        if (!Directory.Exists(stringsDir))
        {
            Directory.CreateDirectory(stringsDir);
        }
        string stringsPath = stringsDir + "/InfoPlist.strings";
        if (!File.Exists(stringsPath))
        {
            File.WriteAllText(stringsPath, "CFBundleDisplayName = \"Legend of the Sword Master\";\n");
        }
        // 将 InfoPlist.strings 添加到 Xcode 工程
        string _projectPath = PBXProject.GetPBXProjectPath(projectPath);
        PBXProject project = new PBXProject();
        project.ReadFromString(File.ReadAllText(_projectPath));
        string targetGUID = project.TargetGuidByName(PBXProject.GetUnityTargetName());
        string relPath = "English.lproj/InfoPlist.strings";
        string fileGuid = project.AddFile(relPath, relPath, PBXSourceTree.Source);
        project.AddFileToBuild(targetGUID, fileGuid);
        // 添加 Sign In with Apple entitlements
        AddEntitlements(projectPath, project, targetGUID);
        File.WriteAllText(_projectPath, project.WriteToString());
    }
    /// <summary>
    /// 添加 Sign In with Apple 等 entitlements
    /// </summary>
    private static void AddEntitlements(string projectPath, PBXProject project, string targetGUID)
    {
        string entPath = projectPath + "/" + PBXProject.GetUnityTargetName() + ".entitlements";
        // 如果 entitlements 文件不存在,创建
        if (!File.Exists(entPath))
        {
            string entContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" +
                "<plist version=\"1.0\">\n" +
                "<dict>\n" +
                "\t<key>com.apple.developer.applesignin</key>\n" +
                "\t<array>\n" +
                "\t\t<string>Default</string>\n" +
                "\t</array>\n" +
                "</dict>\n" +
                "</plist>\n";
            File.WriteAllText(entPath, entContent);
        }
        else
        {
            // 已有 entitlements 文件,检查是否已有 Sign In with Apple
            string existingContent = File.ReadAllText(entPath);
            if (!existingContent.Contains("com.apple.developer.applesignin"))
            {
                existingContent = existingContent.Replace("</dict>",
                    "\t<key>com.apple.developer.applesignin</key>\n" +
                    "\t<array>\n" +
                    "\t\t<string>Default</string>\n" +
                    "\t</array>\n" +
                    "</dict>");
                File.WriteAllText(entPath, existingContent);
            }
        }
        // 将 entitlements 文件加入工程并设置 build property
        string entFileName = PBXProject.GetUnityTargetName() + ".entitlements";
        string fileGuid = project.AddFile(entFileName, entFileName, PBXSourceTree.Source);
        project.AddFileToBuild(targetGUID, fileGuid);
        project.SetBuildProperty(targetGUID, "CODE_SIGN_ENTITLEMENTS", entFileName);
    }