三国卡牌客户端基础资源仓库
hch
2026-06-15 b2ceae0fd6d520fd068de9ddc5d1e9c11da3983d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
using DG.Tweening.Plugins.Core.PathCore;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.iOS.Xcode.Custom;
using UnityEngine;
 
public class XCodeProjectMod
{
    private const string CODE_SIGN_DEVELOPER = "";
    private const string CODE_SIGN_DISTRIBUTION = "";
    private const string PROVISIONING_DEVELOPER = "";
    private const string PROVISIONING_DISTRIBUTION = "";
    private const string TEAM = "";
 
    [UnityEditor.Callbacks.PostProcessBuild(999)]
    public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
    {
        if (buildTarget != BuildTarget.iOS)
        {
            return;
        }
 
 
        DoPBXProject(path);
        BuildPlist(path);
        ModifyFile(path);
 
 
       
    }
 
    static void DoPBXProject(string path)
    {
        string _projectPath = PBXProject.GetPBXProjectPath(path);
 
        PBXProject project = new PBXProject();
        project.ReadFromString(File.ReadAllText(_projectPath));
 
        string targetGUID = project.TargetGuidByName(PBXProject.GetUnityTargetName());
        string fwTargetGUID = project.TargetGuidByName("UnityFramework");
 
        // BuildSetting里的相关设置
        project.SetBuildProperty(project.ProjectGuid(), "ENABLE_BITCODE", "NO");
        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");
 
        // 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);
 
        project.AddFrameworkToProject(fwTargetGUID, "SystemConfiguration.framework", true);
        project.AddFrameworkToProject(fwTargetGUID, "Security.framework", true);
        project.AddFrameworkToProject(fwTargetGUID, "JavaScriptCore.framework", true);
        project.AddFrameworkToProject(fwTargetGUID, "Accelerate.framework", true);
 
        // fimpfunSDK.framework 添加到工程并设置为 Embed & Sign
        string fimpfunFrameworkPath = path + "/Frameworks/fimpfunSDK.framework";
        if (Directory.Exists(fimpfunFrameworkPath))
        {
            string fileGuid = project.AddFile(fimpfunFrameworkPath, "Frameworks/fimpfunSDK.framework", PBXSourceTree.Source);
            project.AddFileToBuild(fwTargetGUID, fileGuid);
            // Embed & Sign: 添加到 main target 的 Embed Frameworks
            project.AddFileToBuild(targetGUID, fileGuid);
        }
 
        // 处理 库文件
        project.AddFileToBuild(fwTargetGUID, project.AddFile("/usr/lib/libz.tbd", "Frameworks/libz.tbd", PBXSourceTree.Sdk));
        project.AddFileToBuild(fwTargetGUID, project.AddFile("/usr/lib/libsqlite3.tbd", "Frameworks/libsqlite3.tbd", PBXSourceTree.Sdk));
        project.AddFileToBuild(fwTargetGUID, project.AddFile("/usr/lib/libc++.tbd", "Frameworks/libc++.tbd", PBXSourceTree.Sdk));
        project.AddFileToBuild(fwTargetGUID, project.AddFile("/usr/lib/libiconv.tbd", "Frameworks/libiconv.tbd", PBXSourceTree.Sdk));
        project.AddFileToBuild(fwTargetGUID, project.AddFile("/usr/lib/libresolv.tbd", "Frameworks/libresolv.tbd", PBXSourceTree.Sdk));
 
        //UnityFramework
        project.AddBuildProperty(fwTargetGUID, "OTHER_LDFLAGS", "-ObjC");
 
        // 添加 GoogleService-Info.plist 和 funshine.plist 到工程
        string iosPluginPath = path + "/Libraries/Plugins/iOS/Channel";
        string[] configPlists = { "GoogleService-Info.plist", "funshine.plist" };
        foreach (var plistName in configPlists)
        {
            string srcPath = iosPluginPath + "/" + plistName;
            if (File.Exists(srcPath))
            {
                string destPath = path + "/" + plistName;
                File.Copy(srcPath, destPath, true);
                string fileGuid = project.AddFile(plistName, plistName, PBXSourceTree.Source);
                project.AddFileToBuild(targetGUID, fileGuid);
            }
        }
 
        // 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)
    {
        string _plistPath = path + "/Info.plist";
 
        PlistDocument _plist = new PlistDocument();
        _plist.ReadFromString(File.ReadAllText(_plistPath));
 
        PlistElementDict _rootDict = _plist.root;
        _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");
 
        // 适配欧盟地区政策法规 - 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();
        dict0.SetString("CFBundleTypeRole", "Editor");
        dict0.SetString("CFBundleURLName", "fb");
        var schemesArray = dict0.CreateArray("CFBundleURLSchemes");
        schemesArray.AddString("fb1445747540428317");
 
        var array1 = _rootDict.CreateArray("LSApplicationQueriesSchemes");
        array1.AddString("fbapi");
        array1.AddString("fb-messenger-api");
        array1.AddString("fbauth2");
        array1.AddString("fbshareextension");
 
        File.WriteAllText(_plistPath, _plist.WriteToString());
    }
 
 
 
    private static void ModifyFile(string projectPath)
    {
        //修改UnityAppController.mm 文件
        var _xclass = new XClass(projectPath + "/Classes/UnityAppController.mm");
        //引入fimpfunSDK
        _xclass.WriteBelow("#include \"PluginBase/AppDelegateListener.h\"", "#import \"fimpfunSDK.h\"");
        //unity交互调用的方法
        string newCode = "\n" +
                 "extern \"C\" void IOSUniyMessageHandle(const char* jsonString) {\n" +
                 "    [GetAppController().fimpfunBridge HandleGameMessage:[NSString stringWithUTF8String:jsonString]];\n" +
                 "}";
        _xclass.WriteBelow("extern \"C\" ScreenOrientation    UnityCurrentOrientation()   { return GetAppController().unityView.contentOrientation; }", newCode);
        //初始化fimpfunSDK
        newCode = "\n" +
                "    _fimpfunBridge = [[FimpfunBridge alloc] init];\n    [_fimpfunBridge YngameSDKInit:application didFinishLaunchingWithOptions:launchOptions];";
        _xclass.WriteBelow("::printf(\"-> applicationDidFinishLaunching()\\n\");", newCode);
 
 
        // 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];");
 
 
        //修改UnityAppController.h 文件
        _xclass = new XClass(projectPath + "/Classes/UnityAppController.h");
        _xclass.WriteBelow("@class DisplayConnection;", "@class FimpfunBridge;");
        _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);
    }
 
    
 
    private static string GetBuildPath()
    {
        string _buildPath = Application.dataPath + "/../IOS_BUILD";
 
        if (!Directory.Exists(_buildPath))
        {
            Directory.CreateDirectory(_buildPath);
        }
 
        return new DirectoryInfo(_buildPath).FullName;
    }
 
    public static string[] GetBuildLevels()
    {
        List<string> _sceneNames = new List<string>();
        foreach (var _scene in EditorBuildSettings.scenes)
        {
            if (_scene == null)
            {
                continue;
            }
 
            if (_scene.enabled)
            {
                _sceneNames.Add(_scene.path);
            }
        }
        return _sceneNames.ToArray();
    }
 
    private static bool s_IsAppend = false;
 
    public static void BuildIOSProject_Append()
    {
        string _buildPath = GetBuildPath();
        UnityEngine.Debug.Log(_buildPath);
        if (string.IsNullOrEmpty(_buildPath))
        {
            return;
        }
 
        s_IsAppend = true;
        PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, ScriptingImplementation.IL2CPP);
        BuildPipeline.BuildPlayer(ClientPackage.baseLevels, _buildPath, BuildTarget.iOS, BuildOptions.AcceptExternalModificationsToPlayer);
    }
 
    public static void BuildIOSProject_Replace()
    {
        s_IsAppend = false;
        PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, ScriptingImplementation.IL2CPP);
        BuildPipeline.BuildPlayer(ClientPackage.baseLevels, GetBuildPath(), BuildTarget.iOS, BuildOptions.None);
    }
 
    [MenuItem("Build/ipa")]
    public static void BuildIPA()
    {
        var p = new System.Diagnostics.Process();
        p.StartInfo.FileName = "osascript";
        p.StartInfo.Arguments = string.Format("-e 'tell application \"Terminal\" \n activate \n do script \"cd {0} && sh {1} {2}\" \n end tell'",
                                               Application.dataPath + "/../",
                                              "buildipa.sh",
                                               GetBuildPath());
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = false;
        p.Start();
        p.WaitForExit();
    }
 
}