Unity通过PBXProject生成XCode工程

1.概论

近来看了点关于Unity导出iOS项目的相关资料,没有看到一个非常全的脚本来进行自动化项目设置,经过几天的尝试,最终产出了这篇文章。Unity提供了非常强大的PBXObject利用C#代码来设置iOS工程的各种设置,官方文档连接:https://docs.unity3d.com/2018.4/Documentation/ScriptReference/iOS.Xcode.PBXProject.html,脚本写好后放在Unity工程里即可,导出的iOS工程可以直接运行,不用再各种设置才能运行了,非常方便,文章第4条.完整示例贴了一个完整的文件供大家参考,项目使用的Unity版本为2018.4.20f

2.详细介绍

a.获取一个iOS工程对象

通过OnPostprocessBuild方法里面的path可以很简单的得到一个PBXObject对象

public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
  string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
  PBXProject proj = new PBXProject();
  proj.ReadFromFile(projPath);
}
b.通过PBXObject获取到目标Target

基本上所有的设置都会在这个目标里完成,懂iOS开发的就知道这个东西我们设置签名,路径(Framework search pathLibrary search pathOther link flags)等的地方

string target = proj.TargetGuidByName("Unity-iPhone");
c.利用target设置具体的参数
c1.设置自动签名

proj.SetBuildProperty(target, "CODE_SIGN_IDENTITY", "Apple Development");
proj.SetBuildProperty(target, "CODE_SIGN_STYLE", "Automatic");
proj.SetTeamId(target, teamId); //teamId 是对应开发者正好的团队id (在苹果后台可以看到)
c2.添加系统的Framework

proj.AddFrameworkToProject(target, "AdSupport.framework", true);
proj.AddFrameworkToProject(target, "CoreTelephony.framework", true);
proj.AddFrameworkToProject(target, "StoreKit.framework", true); //内购需要 否则PBXCapabilityType.InAppPurchase会加不上
c3.设置bitcode和Other link flags

// 设置 BitCode
proj.SetBuildProperty(target, "ENABLE_BITCODE", "false");
// 设置 other link flags -ObjC
proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-ObjC");
c3.添加系统的tbd库 (和添加Framework不一样)

// 自定义一个添加方法
private static void AddLibToProject(PBXProject proj, string target, string lib) {
   string file = proj.AddFile("usr/lib/" + lib, "Frameworks/" + lib, PBXSourceTree.Sdk);
    proj.AddFileToBuild(target, file);
}
// 调用
AddLibToProject(proj,target,"libc++.1.tbd");
AddLibToProject(proj,target,"libresolv.tbd");
c4.添加自定义的动态库(Embed&Sign)

string defaultLocationInProj = Application.dataPath+"/Editor/Plugins/iOS";     //framework 存放的路径
const string coreFrameworkName = "boxjing.framework";  // framework 的文件名
string framework = Path.Combine(defaultLocationInProj, coreFrameworkName);
string fileGuid = proj.AddFile(framework, "Frameworks/" + coreFrameworkName, PBXSourceTree.Sdk);
PBXProjectExtensions.AddFileToEmbedFrameworks(proj, target, fileGuid);
proj.SetBuildProperty(target, "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks");
c5.添加内购和推送,这里需要把正确运行工程的entitlements文件复制到工程里一份,后面让代码自动再复制进iOS工程里使用

proj.AddCapability(target, PBXCapabilityType.InAppPurchase);
// 添加 Capabilities  -  推送
string entitlement = Application.dataPath+ "/Editor/AppleNative/Unity-iPhone.entitlements";
File.Copy(entitlement, path+"/Unity-iPhone.entitlements");
proj.AddCapability(target, PBXCapabilityType.PushNotifications,"Unity-iPhone.entitlements",true);
d.修改Info.plist文件

正常的工程打包时候都需要设置version和build,以及添加项目所使用的某些权限描述,如相册、麦克风等,其根本都是修改Info.plist文件
Info.plist文件比较特殊,其根本就是一个map,方便修改可以直接将其读出来作为Map进行操作:

string plistPath = path + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath));
PlistElementDict infoDict = plist.root;

在所有的修改完毕后,需要调用保存:

File.WriteAllText(plistPath, plist.WriteToString());
d1.修改version和build

infoDict.SetString("CFBundleShortVersionString",version);   //version
infoDict.SetString("CFBundleVersion",build);                 //build
d2.增加权限描述

// 权限  根据自己的项目 修改后面的提示文案
infoDict.SetString("NSLocationWhenInUseUsageDescription", "为了发现周围的好友,请允许App访问您的地里位置"); //地理位置
infoDict.SetString("NSPhotoLibraryUsageDescription", "为了能选择照片进行上传,请允许App访问您的相册");      //相册
infoDict.SetString("NSMicrophoneUsageDescription", "为了能够录制声音,请允许App访问您的麦克风权限");     //麦克风

3.特殊需求

在开发的过程中可能会遇到一下,需要修改iOS原生代码来实现的功能,比如项目中集成了极光推送,需要在UnityAppController.mm中增加一些代码和在Preprocessor.h中修改一个宏定义值来实现,这里可以提供两种办法,一种是用脚本直接用另一个写好的UnityAppController.mm文件替换Unity打包出来的工程里面的文件,还有一种就是直接在文件中进行字符串替换。

a.整个文件的替换

string UnityAppControllerMM     = Application.dataPath + "/Editor/AppleNative/UnityAppController.mm"; //获取到我们自己的代码文件路径
 string tagUnityAppControllerMM  = path + "/Classes/UnityAppController.mm"; //要替换掉工程里面的文件路径
if (File.Exists(tagUnityAppControllerMM)) //如果有 先删除掉 再复制过去
{
     File.Delete(tagUnityAppControllerMM);
}
File.Copy(UnityAppControllerMM, tagUnityAppControllerMM);
b.替换部分代码或者在某一行后面插入代码

这里需要封装一个文件操作类,方便修改代码:

// 定义文件更新类
public partial class XClass : System.IDisposable
{
        private string filePath;
        public XClass(string fPath) //通过文件路径初始化对象
        {
            filePath = fPath;
            if( !System.IO.File.Exists( filePath ) ) {
                Debug.LogError( filePath +"该文件不存在,请检查路径!" );
                return;
            }
        }
      // 替换某些字符串
        public void ReplaceString(string oldStr,string newStr,string method="")  
        {  
            if (!File.Exists (filePath))   
            {  
                return;  
            }  
            bool getMethod = false;  
            string[] codes = File.ReadAllLines (filePath);  
            for (int i=0; i<codes.Length; i++)   
            {  
                string str=codes[i].ToString();  
                if(string.IsNullOrEmpty(method))  
                {  
                    if(str.Contains(oldStr))codes.SetValue(newStr,i);  
                }  
                else  
                {  
                    if(!getMethod)  
                    {  
                        getMethod=str.Contains(method);  
                    }  
                    if(!getMethod)continue;  
                    if(str.Contains(oldStr))  
                    {  
                        codes.SetValue(newStr,i);  
                        break;  
                    }  
                }  
            }  
            File.WriteAllLines (filePath, codes);  
        }  
      // 在某一行后面插入代码
        public void WriteBelowCode(string below, string text)
        {
            StreamReader streamReader = new StreamReader(filePath);
            string text_all = streamReader.ReadToEnd();
            streamReader.Close();

            int beginIndex = text_all.IndexOf(below);
            if(beginIndex == -1){
                return; 
            }
            int endIndex = text_all.LastIndexOf("\n", beginIndex + below.Length);

            text_all = text_all.Substring(0, endIndex) + "\n"+text+"\n" + text_all.Substring(endIndex);

            StreamWriter streamWriter = new StreamWriter(filePath);
            streamWriter.Write(text_all);
            streamWriter.Close();
        }
        public void Dispose()
        {

        }
}

使用的时候非常方便:

//Preprocessor.h文件
 XClass Preprocessor = new XClass(filePath + "/Classes/Preprocessor.h");
 //在指定代码后面增加一行代码
Preprocessor.WriteBelowCode("要增加的位置前一行代码","要增加的代码");
 //在指定代码中替换一行   允许远程推送
Preprocessor.ReplaceString("#define UNITY_USES_REMOTE_NOTIFICATIONS 0","#define UNITY_USES_REMOTE_NOTIFICATIONS 1");

4.完整示例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using UnityEditor.iOS.Xcode.Extensions;
using System.Text.RegularExpressions;

#if UNITY_IOS || UNITY_EDITOR
public class XcodeBuildPostprocessor
{
    // [PostProcessBuildAttribute(88)]
    [PostProcessBuild]
    public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
    {
        if (buildTarget == BuildTarget.iOS)
        {
            UnityEngine.Debug.Log("XCodePostProcess: Starting to perform post build tasks for iOS platform.");
            // 修改项目设置 如Bitcode Framework *tbd 等
            ProjectSetting(path,"xxxxxxx"); // 后面参数TeamId 换成自己的TeamID 用来自动签名

            // 修改Info.Plist文件  如权限  version  build
            InfoPlist(path,"1.0.0","20"); //后面两个参数依次为 version、build

            // 替换原生代码文件
            // ReplaceNativeCodeFile(path);  //替换文件  如 xxx的  UnityAppController.mm (加了推送相关代码)  也可以使用EditNativeCode里面的方法实现

            // 修改原生代码文件里的部分代码 追加、插入和替换
            EditNativeCode(path);   //修改文件中的代码  如 xxx的Preprocessor.h中  UNITY_USES_REMOTE_NOTIFICATIONS 改为1 推送使用

        }
    }
    private static void ProjectSetting(string path,string teamId) {
        // 主要官方文档  https://docs.unity3d.com/cn/2018.4/ScriptReference/iOS.Xcode.PBXProject.html
        string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
        PBXProject proj = new PBXProject();
        proj.ReadFromFile(projPath);

        string target = proj.TargetGuidByName("Unity-iPhone");

        // 证书设置  自动签名
        proj.SetBuildProperty(target, "CODE_SIGN_IDENTITY", "Apple Development");
        proj.SetBuildProperty(target, "CODE_SIGN_STYLE", "Automatic");
        proj.SetTeamId(target, teamId);

        // add extra framework(s) 
        proj.AddFrameworkToProject(target, "AdSupport.framework", true);
        proj.AddFrameworkToProject(target, "CoreTelephony.framework", true);
        proj.AddFrameworkToProject(target, "StoreKit.framework", true); //内购需要 否则PBXCapabilityType.InAppPurchase会加不上

        // XXXX的 tersafe2.framework 动态库 需要设置 Embed&Sign
        string defaultLocationInProj = Application.dataPath+"/Editor/Plugins/iOS";     //framework 存放的路径
        const string coreFrameworkName = "tersafe2.framework";  // framework 的文件名
        string framework = Path.Combine(defaultLocationInProj, coreFrameworkName);
        string fileGuid = proj.AddFile(framework, "Frameworks/" + coreFrameworkName, PBXSourceTree.Sdk);
        PBXProjectExtensions.AddFileToEmbedFrameworks(proj, target, fileGuid);
        proj.SetBuildProperty(target, "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks");

        // 添加 Capabilities  -  内购
        proj.AddCapability(target, PBXCapabilityType.InAppPurchase);
        // 添加 Capabilities  -  推送
        string entitlement = Application.dataPath+ "/Editor/AppleNative/Unity-iPhone.entitlements";
        File.Copy(entitlement, path+"/Unity-iPhone.entitlements");
        proj.AddCapability(target, PBXCapabilityType.PushNotifications,"Unity-iPhone.entitlements",true);

        // 自定义资源 如自定义代码中使用到的图片资源等
        // AddCustomResource(path,proj,target,"xxx.png");

        // 设置 BitCode
        proj.SetBuildProperty(target, "ENABLE_BITCODE", "false");
        
        // 设置 other link flags -ObjC
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-ObjC");

        // add other files like *.tbd
        AddLibToProject(proj,target,"libc++.1.tbd");
        AddLibToProject(proj,target,"libresolv.tbd");

        // 保存
        File.WriteAllText(projPath, proj.WriteToString());
    }
    // 添加lib方法
    private static void AddLibToProject(PBXProject proj, string target, string lib) {
        string file = proj.AddFile("usr/lib/" + lib, "Frameworks/" + lib, PBXSourceTree.Sdk);
        proj.AddFileToBuild(target, file);
    }
    // 添加自定义资源  如自定义代码中使用的图片  资源放在 Assets/Editor/AppleNative 中
    private static void AddCustomResource(string path, PBXProject proj, string target, string fileName)
    {
        string customImagePath = Application.dataPath+ "/Editor/AppleNative/"+fileName;
        File.Copy(customImagePath, path+"/"+fileName);
        string file = proj.AddFile(path+"/"+fileName, fileName , PBXSourceTree.Source);
        proj.AddFileToBuild(target, file);
    }
    // 修改Info.Plist
    private static void InfoPlist(string path,string version,string build) {
        string plistPath = path + "/Info.plist";
        PlistDocument plist = new PlistDocument();
        plist.ReadFromString(File.ReadAllText(plistPath));
        // Get root
        PlistElementDict infoDict = plist.root;

        // 版本
        infoDict.SetString("CFBundleShortVersionString",version);   //version
        infoDict.SetString("CFBundleVersion",build);                 //build

        // 权限
        infoDict.SetString("NSLocationWhenInUseUsageDescription", "为了发现周围的好友,请允许App访问您的地里位置"); //地理位置
        infoDict.SetString("NSPhotoLibraryUsageDescription", "为了能选择照片进行上传,请允许App访问您的相册");      //相册
        // infoDict.SetString("NSMicrophoneUsageDescription", "为了能够录制声音,请允许App访问您的麦克风权限");     //麦克风

        // 设置BackgroundMode 远程推送
        PlistElementArray bmArray = null;
        if (!infoDict.values.ContainsKey("UIBackgroundModes"))
            bmArray = infoDict.CreateArray("UIBackgroundModes");
        else
            bmArray = infoDict.values["UIBackgroundModes"].AsArray();
        bmArray.values.Clear();
        bmArray.AddString("remote-notification");

        // 允许HTTP请求 不设置的话 只能全部HTTPS请求
        var atsKey = "NSAppTransportSecurity";
        PlistElementDict dictTmp = infoDict.CreateDict(atsKey);
        dictTmp.SetBoolean("NSAllowsArbitraryLoads", true);

        /*------如果要设置scheme白名单 请放开该段 追加自己需要添加的白名单 下面举例为微信分享所需要的白名单-----*/
        /*
        // 增加白名单 scheme   打开别的app需要  比如 分享到微信 需要添加 wechat、weixin、weixinULAPI
        PlistElement array = null;
        if (infoDict.values.ContainsKey("LSApplicationQueriesSchemes"))
        {
            array = infoDict["LSApplicationQueriesSchemes"].AsArray();
        }
        else
        {
            array = infoDict.CreateArray("LSApplicationQueriesSchemes");
        }
        infoDict.values.TryGetValue("LSApplicationQueriesSchemes", out array);
        PlistElementArray Qchemes = array.AsArray();
        Qchemes.AddString("wechat");
        Qchemes.AddString("weixin");
        Qchemes.AddString("weixinULAPI");
        */

        // 保存
        File.WriteAllText(plistPath, plist.WriteToString());
    }
    // 替换文件
    private static void ReplaceNativeCodeFile(string path)
    {
        string UnityAppControllerMM     = Application.dataPath + "/Editor/AppleNative/UnityAppController.mm";
        string tagUnityAppControllerMM  = path + "/Classes/UnityAppController.mm";
        if (File.Exists(tagUnityAppControllerMM))
        {
            File.Delete(tagUnityAppControllerMM);
        }
        File.Copy(UnityAppControllerMM, tagUnityAppControllerMM);
    }
    // 修改部分代码
    private static void EditNativeCode(string filePath)
    {
        //Preprocessor.h文件
        XClass Preprocessor = new XClass(filePath + "/Classes/Preprocessor.h");
 
        //在指定代码后面增加一行代码
        // Preprocessor.WriteBelowCode("要增加的位置前一行代码","要增加的代码");
 
        //在指定代码中替换一行  xxxxxx 允许远程推送
        Preprocessor.ReplaceString("#define UNITY_USES_REMOTE_NOTIFICATIONS 0","#define UNITY_USES_REMOTE_NOTIFICATIONS 1");
 
        //在指定代码后面增加一行
        // Preprocessor.WriteBelowCode("UnityCleanup();\n}","- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url\r{\r    return [ShareSDK handleOpenURL:url wxDelegate:nil];\r}");

        XClass AppController = new XClass(filePath + "/Classes/UnityAppController.mm");
        AppController.WriteBelow("#include <sys/sysctl.h>","\n#import \"JPUSHService.h\"");
        AppController.WriteBelow("UnitySendDeviceToken(deviceToken);","    [JPUSHService registerDeviceToken:deviceToken];");
 
    }


    // 定义文件更新类
    public partial class XClass : System.IDisposable
    {

        private string filePath;

        public XClass(string fPath) //通过文件路径初始化对象
        {
            filePath = fPath;
            if( !System.IO.File.Exists( filePath ) ) {
                Debug.LogError( filePath +"该文件不存在,请检查路径!" );
                return;
            }
        }
        // 替换某些字符串
        public void ReplaceString(string oldStr,string newStr,string method="")  
        {  
            if (!File.Exists (filePath))   
            {  

                return;  
            }  
            bool getMethod = false;  
            string[] codes = File.ReadAllLines (filePath);  
            for (int i=0; i<codes.Length; i++)   
            {  
                string str=codes[i].ToString();  
                if(string.IsNullOrEmpty(method))  
                {  
                    if(str.Contains(oldStr))codes.SetValue(newStr,i);  
                }  
                else  
                {  
                    if(!getMethod)  
                    {  
                        getMethod=str.Contains(method);  
                    }  
                    if(!getMethod)continue;  
                    if(str.Contains(oldStr))  
                    {  
                        codes.SetValue(newStr,i);  
                        break;  
                    }  
                }  
            }  
            File.WriteAllLines (filePath, codes);  
        }  

        // 在某一行后面插入代码
        public void WriteBelowCode(string below, string text)
        {
            StreamReader streamReader = new StreamReader(filePath);
            string text_all = streamReader.ReadToEnd();
            streamReader.Close();

            int beginIndex = text_all.IndexOf(below);
            if(beginIndex == -1){

                return; 
            }

            int endIndex = text_all.LastIndexOf("\n", beginIndex + below.Length);

            text_all = text_all.Substring(0, endIndex) + "\n"+text+"\n" + text_all.Substring(endIndex);

            StreamWriter streamWriter = new StreamWriter(filePath);
            streamWriter.Write(text_all);
            streamWriter.Close();
        }
        public void Dispose()
        {

        }
    }
}
#endif

写在结尾

后面可以再看看,能不能在这里面直接加入自动化打包ipa的脚本,这样的话,Unity开发者连Xcode开发工具都不用打开了,可以直接传内部测试或传App Store进行审核发布。



作者:BoxJing
链接:https://www.jianshu.com/p/a25a60b9991e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

<think> 我们正在处理一个Unity iOS原生插件的问题。之前,用户按照要求创建了Plugins/iOS/DiskSpaceBridge.mm文件,但在运行时遇到了EntryPointNotFoundException: GetFreeDiskSpace_iOS错误。我们已经提供了一系列的解决步骤,其中一步是生成Xcode工程后检查Build Phases > Compile Sources,确认DiskSpaceBridge.mm是否在列表中。现在,用户询问如何执行这一步。 根据引用[1]和引用[2],我们知道Unity导出Xcode工程的基本流程。用户需要先将Unity项目切换到iOS平台,然后导出Xcode工程。导出后,在Xcode中打开工程,检查Build Phases中的Compile Sources是否包含了DiskSpaceBridge.mm文件。 具体步骤如下: 1. **导出Xcode工程**: 在Unity中,选择File -> Build Settings,切换到iOS平台,然后点击Build按钮,选择一个目录导出Xcode工程(引用[1][2])。 2. **打开Xcode工程**: 在导出的目录中找到Unity-iPhone.xcodeproj文件,双击使用Xcode打开(引用[1])。 3. **检查Compile Sources**: - 在Xcode中,选择工程导航器(左侧面板),确保你选择了项目(顶级节点)。 - 选择目标(通常为Unity-iPhone),然后切换到“Build Phases”选项卡。 - 展开“Compile Sources”部分,查看其中是否包含DiskSpaceBridge.mm文件(引用[1])。 4. **如果不在列表中,手动添加**: 如果DiskSpaceBridge.mm不在Compile Sources列表中,可以手动添加: - 点击“Compile Sources”下方的“+”按钮。 - 在弹出的文件选择对话框中,找到DiskSpaceBridge.mm文件(它应该位于导出的Xcode工程中的Classes文件夹下,或者Plugins/iOS文件夹下,取决于Unity的导出结构),选中并点击“Add”。 5. **重新构建工程**: 添加后,重新构建Xcode工程,确保没有编译错误。 6. **真机测试**: 连接iOS设备,运行测试,看是否解决了EntryPointNotFoundException错误(引用[2])。 另外,根据引用[5],我们还可以使用Unity的PostProcessBuild脚本来确保在导出Xcode工程后自动修改工程配置(如添加文件到编译源),但这里我们主要关注手动检查。 如果用户发现文件确实不在Compile Sources中,手动添加后通常可以解决问题。如果文件已经在列表中,那么问题可能出在其他地方(如函数签名、DllImport声明等),需要进一步排查。 注意:在导出Xcode工程后,Unity会自动将Plugins/iOS下的文件复制到Xcode工程中,但有时可能会因为某些原因没有被添加到编译源中。 下面,我们将详细说明在Xcode中检查的步骤,并附上截图说明(由于文本无法提供截图,将用文字描述)。 步骤详解: 1. **打开Xcode工程**: 导出的Xcode工程目录结构如引用[1]所述,双击Unity-iPhone.xcodeproj打开。 2. **导航到Build Phases**: 在Xcode中,点击左侧导航栏的工程名称(Unity-iPhone),然后在中间区域选择“Build Phases”选项卡。 3. **查看Compile Sources**: 在Build Phases中,找到“Compile Sources”部分,展开它,查看列表。列表中是所有参与编译的源文件(.m, .mm, .c, .cpp等)。我们需要确保DiskSpaceBridge.mm在这个列表中。 4. **手动添加(如果需要)**: 如果DiskSpaceBridge.mm不在列表中,点击“Compile Sources”下方的“+”按钮,然后浏览到文件所在位置(通常在Classes文件夹下,或者Plugins/iOS文件夹下),选择DiskSpaceBridge.mm,点击“Add”。 5. **确保文件物理存在**: 在Xcode工程中,我们还需要确认文件在项目目录中。在Xcode左侧的项目导航器中,检查Classes或Plugins/iOS文件夹下是否有DiskSpaceBridge.mm。如果文件显示红色,说明文件不存在于该路径,需要重新导入(引用[1]中提到了导入文件的两种方式)。 6. **重新编译运行**: 完成添加后,重新编译运行,看错误是否消失。 另外,我们还可以通过以下方式确保文件被正确包含: - 在Xcode中,选择项目导航器,右键点击Classes文件夹(或Plugins/iOS文件夹),选择“Add Files to 'Unity-iPhone'...”,然后重新添加DiskSpaceBridge.mm,注意选择“Create groups”方式(引用[1])。 如果按照上述步骤操作后,问题仍然存在,可能需要检查函数导出名称(注意C++函数在导出时可能会被修饰,因此我们之前已经使用了`extern "C"`来避免)以及C#中调用的函数名是否正确。 最后,如果用户遇到其他问题,比如引用[4]中提到的头文件找不到错误,可能需要检查Xcode的Header Search Paths设置,但本例中不涉及此问题。 总结:检查Xcode工程的Compile Sources列表是解决EntryPointNotFoundException的重要步骤之一,确保原生代码被正确编译进应用程序。 下面,我们将生成相关问题。 </think>### 生成Xcode工程后检查编译源文件列表的完整指南 要解决`EntryPointNotFoundException: GetFreeDiskSpace_iOS`错误,请按以下步骤操作: #### 1. 导出Xcode工程前准备 ```csharp // 在Unity编辑器中检查插件路径 using UnityEditor; using System.IO; [InitializeOnLoad] public class PluginValidator { static PluginValidator() { string pluginPath = "Assets/Plugins/iOS/DiskSpaceBridge.mm"; if(!File.Exists(pluginPath)) { Debug.LogError($"插件文件丢失: {pluginPath}"); } else { Debug.Log($"iOS插件位置验证通过: {pluginPath}"); } } } ``` - 确保`DiskSpaceBridge.mm`位于`Assets/Plugins/iOS/`目录[^1][^2] - 在Unity Inspector中确认iOS平台已启用 #### 2. 导出Xcode工程 1. Unity菜单:`File > Build Settings` 2. 选择iOS平台并点击`Switch Platform` 3. 设置Bundle Identifier和版本号[^1] 4. 点击`Build`导出Xcode工程[^2] #### 3. 在Xcode中验证编译源文件 1. **打开Xcode工程**: - 双击导出的`Unity-iPhone.xcodeproj`文件[^1] 2. **检查编译源文件**: ```mermaid graph TD A[打开Xcode工程] --> B[选择项目导航器] B --> C[选择Unity-iPhone目标] C --> D[转到Build Phases选项卡] D --> E[展开Compile Sources] E --> F{检查DiskSpaceBridge.mm是否存在} F -->|存在| G[继续步骤4] F -->|不存在| H[手动添加] ``` 3. **手动添加文件(如缺失)**: - 右键点击`Classes`文件夹 → `Add Files to "Unity-iPhone"` - 选择`DiskSpaceBridge.mm` → 勾选`Add to targets`[^1] - 选择`Create groups`选项(非文件夹引用)[^1] #### 4. 关键配置检查 1. **Linker Flags**: - `Build Settings > Other Linker Flags`添加`-ObjC` 2. **文件存在性验证**: - 在Xcode终端执行: ```bash find . -name DiskSpaceBridge.mm # 应输出类似: ./Classes/Plugins/iOS/DiskSpaceBridge.mm ``` 3. **编译测试**: - 按`Cmd+B`编译工程,检查是否有链接错误 #### 5. 自动化验证脚本(推荐) ```csharp // 后处理脚本确保文件被包含 #if UNITY_EDITOR using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; public class BuildPostprocessor { [PostProcessBuild(999)] public static void OnPostprocessBuild(BuildTarget target, string path) { if (target == BuildTarget.iOS) { PBXProject project = new PBXProject(); string projPath = PBXProject.GetPBXProjectPath(path); project.ReadFromFile(projPath); string targetGuid = project.GetUnityMainTargetGuid(); // 添加编译源文件 string fileGuid = project.AddFile("Classes/Plugins/iOS/DiskSpaceBridge.mm", "Plugins/iOS/DiskSpaceBridge.mm"); project.AddFileToBuild(targetGuid, fileGuid); project.WriteToFile(projPath); } } } #endif ``` #### 6. 真机测试验证 1. 连接iOS设备,选择`Generic iOS Device` 2. 点击`Product > Build`编译 3. 检查控制台输出: - 成功时应无`Undefined symbol: _GetFreeDiskSpace_iOS`错误 - 出现`Disk space: [value]`表示成功调用[^2] > **重要提示**:如果文件已在Compile Sources中但仍报错,请检查函数签名是否完全匹配(包括下划线前缀)[^1],并确认未启用Bitcode(在Build Settings中设`Enable Bitcode = NO`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值