专栏首页游戏开发之旅Unity打iOS包之xcodeapi的使用

Unity打iOS包之xcodeapi的使用

xcodeapi

https://github.com/onevcat/XUPorter

https://bitbucket.org/Unity-Technologies/xcodeapi

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode.Custom;
 
public class ProjectPostProcess
{
    [PostProcessBuildAttribute(1)]
    public static void OnPostProcessBuild(BuildTarget buildTarget, string pathToBuiltProject)
    {
        // 只处理IOS工程, pathToBuildProject会传入导出的ios工程的根目录
        if (buildTarget != BuildTarget.iOS)
            return;
 
        // 创建工程设置对象
        var projectPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
        PBXProject project = new PBXProject();
        project.ReadFromFile(projectPath);
        string targetGuid = project.TargetGuidByName("Unity-iPhone");
 
        // 修改BITCODE设置的例子
        project.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
 
        // 你可能会设置的--------------------------------------------------
        //project.SetBuildProperty(targetGuid, "CODE_SIGN_IDENTITY", "p12证书的code_sign");
        // 把证书设置设置为手动,即不使用Automatically manage signing
        //project.SetTargetAttributes("ProvisioningStyle","Manual");
 
        //project.SetBuildProperty(targetGuid, "PROVISIONING_PROFILE", "mobileprovison文件的UUID");
        //project.SetBuildProperty(targetGuid, "PROVISIONING_PROFILE_SPECIFIER", "mobileprovison文件的Name");
 
        //project.SetTeamId(targetGuid,"证书的TeamId");
        //project.SetBuildProperty(targetGuid, "IPHONEOS_DEPLOYMENT_TARGET", "8.0");
 
        // 添加framework
        //project.AddFrameworkToProject(targetGuid, "StoreKit.framework", true);
        //----------------------------------------------------------------
 
        // 修改后的内容写回到配置文件
        File.WriteAllText(projectPath, project.WriteToString());
 
        // 修改Info.plist的示例
        var plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
        var plist = new PlistDocument();
        plist.ReadFromFile(plistPath);
 
        // 增加字符串类型的设置
        plist.root.SetString("fieldname", "value");
    
        // 修改后的内容写回到文件Info.plist
        plist.WriteToFile(plistPath);
    }
}

关于这个方法的属性,网上的有些例子上写的是PostProcessBuild,其实只是PostProcessBuildAttribute的简写,意义是一样的,不需要纠结。

常用的的Property:

Property

备注

CODE_SIGN_IDENTITY

p12证书的code_sign

在钥匙串那里看双击钥匙串里已安装的证书最上面显示的标题就code_sign也叫“常用名称”

DEVELOPMENT_TEAM

证书的TeamId

在钥匙串那里看,查看方法同上,“组织单位”的值就是TeamId使用project.SetTeamId接口进行设置

ENABEL_BITCODE

YES / NO

关于Bitcode:https://www.cnblogs.com/QianChia/p/6223047.html

IPHONEOS_DEPLOYMENT_TARGET

8.0/8.1/8.2/…/12.1

Unity打出来的默认是6.0目前最低必须设置为8.0以上

PRODUCT_BUNDLE_IDENTIFIER

项目的bundleId

com.xxx.xxxx

PRODUCT_NAME

项目的名称

PROVISIONING_PROFILE

.mobileprovision的UUID

用vim打开.mobileprovision文件然后查找UUID:在vim中输入/UUID,然后按回车键

PROVISIONING_PROFILE_SPECIFIER

mobileprovision的Name

用vim打开.mobileprovision文件然后查找Name:在vim中输入/UUID,然后按回车键

一步到位

如果想要一步到位,导出xcode后自动执行Archive和生产ipa,可以写好shell脚本自动运行

Archive脚本

xcodebuild -project “工程目录/Unity-iPhone.xcodeproj” -scheme Unity-iPhone archive -archivePath "Archive的目录"

生成ipa脚本

xcodebuild -project “工程目录/Unity-iPhone.xcodeproj” -exportArchive -exportFormat ipa -archivePath “Archive的目录” -exportPath “ipa的目录” -exportProvisioningProfile "mobileprovision文件的Name"

\color{red}{Xcode7之后取消了原来的-exportFormat,而是使用exportOptionsPlist 来取代}
\color{red}{改用下面的命令}

xcodebuild -exportArchive -exportOptionsPlist xxx/Info.plist -archivePath xxx.xcarchive -exportPath ~/Desktop/

xcodebuild -exportArchive -exportOptionsPlist …/ExportOptions.plist -archivePath …/testPacking.xcarchive -exportPath ./autoPackage -allowProvisioningUpdates

我们需要自己创建一个plist作为Export Options

写个工具类方便生成

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
 
 
class XcodeExportPlist
{
 
    /// <summary>
    /// 对应ios_build_config.json的exportArchiveMethod
    /// </summary>
    public static readonly string[] kMethods =
        {
            "app-store",
            "enterprise",
            "ad-hoc",
            "development",
        };
 
    public static void GenFile(string filPath, string teamID, string method, string bundleID, string profileName)
    {
        string text = GenText(teamID, method, bundleID, profileName);
        System.IO.File.WriteAllText(filPath, text);
        GameLogger.LogGreen("GenFile: " + filPath);
    }
 
 
    static string GenText(string teamID, string method, string bundleID, string profileName)
    {
        var sb = new System.Text.StringBuilder();
        sb.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        sb.Append("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
        sb.Append("<plist version=\"1.0\">\n");
        sb.Append("<dict>\n");
 
        AppendTeamID(sb, teamID);
        AppendMethod(sb, method);
        AppendUploadSymbols(sb, false);
        AppendProfiles(sb, bundleID, profileName);
        sb.Append("</dict>\n");
        sb.Append("</plist>\n");
        return sb.ToString();
    }
 
    static void AppendTeamID(System.Text.StringBuilder sb, string teamID, string ind = "\t")
    {
        sb.Append(ind).Append("<key>teamID</key>\n");
        AppendPStr(sb, teamID, ind);
    }
 
    static void AppendMethod(System.Text.StringBuilder sb, string method, string ind = "\t")
    {
        sb.Append(ind).Append("<key>method</key>\n");
        AppendPStr(sb, method, ind);
    }
 
    static void AppendUploadSymbols(System.Text.StringBuilder sb, bool isUploadSymbols, string ind = "\t")
    {
        sb.Append(ind).Append("<key>uploadSymbols</key>\n");
        AppendPBool(sb, isUploadSymbols, ind);
    }
 
    static void AppendProfiles(System.Text.StringBuilder sb, string bundleID, string profilesName, string ind = "\t")
    {
        Dictionary<string, string> dic = new Dictionary<string, string>();
        if (!string.IsNullOrEmpty(bundleID))
        {
            dic.Add(bundleID, profilesName);
        }
        if (dic.Count > 0)
        {
            sb.Append(ind).Append("<key>provisioningProfiles</key>\n");
            AppendPDict(sb, dic, ind);
        }
    }
 
    static void AppendPStr(System.Text.StringBuilder sb, string s, string ind)
    {
        sb.Append(ind);
        sb.Append("<string>");
        sb.Append(s);
        sb.Append("</string>\n");
    }
 
    static void AppendPBool(System.Text.StringBuilder sb, bool b, string ind)
    {
        sb.Append(ind).Append(b ? "<true/>" : "<false/>").Append("\n");
    }
 
    static void AppendPDict(System.Text.StringBuilder sb, Dictionary<string, string> key_value, string ind)
    {
        sb.Append(ind);
        sb.Append("<dict>");
        foreach (var item in key_value)
        {
            sb.Append("\n").Append(ind);
            sb.Append("<key>");
            sb.Append(item.Key);
            sb.Append("</key>");
            sb.Append("\n").Append(ind);
            sb.Append("<string>");
            sb.Append(item.Value);
            sb.Append("</string>");
        }
        sb.Append("\n").Append(ind);
        sb.Append("</dict>\n");
    }
}// end class XcodeExportPlist 

结合《Unity中C#如何执行cmd命令(System.Diagnostics.Process的使用)》

https://blog.csdn.net/linxinfa/article/details/52982384 用上面的XcodeExportPlist工具类生成exportOption.plist

public static string GenArchiveOptPlist()
{
    var path = Application.dataPath.Replace("Assets", "Bin/exportOption.plist";
    var teamId = "BQCHO456";
    // "app-store","enterprise","ad-hoc","development"
    var archiveMethod = "enterprise";
    var bundleId = "com.linxinfa.test";
    var provisionName = "linxinfaEnt2019614";
 
    XcodeExportPlist.GenFile(path, teamId, archiveMethod, bundleId, provisionName);
    return path;
}

可以通过C#执行shell命令

//var version = "1.0.0";
//var binPath = Application.dataPath + "/../Bin";
//var xcodeProjPath = binPath + "/XcodeProj";
//var xcodeArchPath = binPath + "/Archives/Unity-iPhone-archive.xcarchive";
//var outputPath = string.Format("{0}/{1}/", binPath, version);
//var appName = string.Format("mygame_v{0}_{1}.ipa", version, DateTime.Now.ToString("yyyyMMdd"));
 
 
private static void XCodeArchive(string xcodeProjPath, string xcodeArchPath)
{
    Debug.Log("XCodeArchive Begin");
    string args = string.Format("-project \"{0}/Unity-iPhone.xcodeproj\" -scheme Unity-iPhone archive -archivePath \"{1}\" ", xcodeProjPath, xcodeArchPath);
    var r = EdtUtil.RunCmd("xcodebuild", args);
 
    if (string.IsNullOrEmpty(r[0]) || r[0].LastIndexOf("ARCHIVE SUCCEEDED") < 0)
    {
        Debug.LogError("Build IPA : XCodeBuildArchive FAILED\n" + r[0]);
        throw new Exception("Build IPA : XCodeBuildArchive FAILED");
    }
    Debug.LogGreen("Build IPA : XCodeBuildArchive OK");
}
 
 
private static void XCodeExportIPA(string xcodeProjPath, string xcodeArchPath, string outputPath, string ipaName)
{
    // 生成plist
    var expOptPath = GenArchiveOptPlist();
 
    Debug.Log("XCodeExportIPA Begin\n xcodeProjPath: " + xcodeProjPath + "\nxcodeArchPath: " + xcodeArchPath + "\noutputPath: " + outputPath);
    string args = string.Format("-project \"{0}/Unity-iPhone.xcodeproj\" -exportArchive -archivePath \"{1}\" -exportPath \"{2}\" -exportOptionsPlist \"{3}\" ",
                                    xcodeProjPath, xcodeArchPath, outputPath, expOptPath);
    var r = EdtUtil.RunCmd("xcodebuild", args);
 
    if (string.IsNullOrEmpty(r[0]) || r[0].LastIndexOf("EXPORT SUCCEEDED") < 0)
    {
        Debug.LogError("Build IPA : XCodeBuildIPA FAILED\n" + r[0]);
        throw new Exception("Build IPA : XCodeBuildIPA FAILED");
    }
    Debug.LogGreen("Build IPA : XCodeBuild IPA OK");
 
    // 重命名
    File.Move(outputPath + "Unity-iPhone.ipa", outputPath + ipaName);
}

如果不放心,也可以到XCode进行手动ipa打包

Archive:

先检查Product->Scheme->Edit Scheme…中的设置是否正确,比如现在是要打的是面向外部玩家的版本,则要Run里面要选择Relaese,Archive里也要选择Release,记得勾选Archive的Options的Reveal Archive in Organizer,检查工程的General中的证书设置是否正确,检查工程的Images.xcassets中的AppIcon的图标设置是否正常

检查无误之后,就可以执行Archive了: Product->Archive

导出ipa:

Archive完之后,会自动弹出Organizer窗口,如果没有弹出来,则手动打开:Window->Organizer

然后点击Distribute App按钮,接下来一步一步Next,注意中间会让你选择一些选项,根据需求进行选择即可

补充:

如果要对不同的配置做修改,比如,单独修改Debug版本或者Release版本的配置项,那么就需要用BuildConfigByName先获取到指定的配置项,然后使用SetBuildPropertyForConfig设置对应的配置项,例如,单独修改debug版本的ENABLE_BITCODE设置:

string configGuid = project.BuildConfigByName("targetGuid", "Debug");
project.SetBuildPropertyForConfig(configGuid, "ENABLE_BITCODE", "NO");

另外,对于XUPorter的导出插件和native代码的功能,Unity 5之后可以直接把需要的文件放在Assets/Plugins/iOS目录下面,文件就会被自动导出到目标工程中,请参考Unity文档:https://docs.unity3d.com/Manual/PluginsForIOS.html 相关参考:https://www.cnblogs.com/pandawuwyj/p/6904770.html

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/CJB_King复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • Unity使用心得 之 ✨ Unity2019打包apk显示版本过低问题

    可能不止2019有问题,正常用unity打包windows是没问题的,选择打包安卓就会出现下图所示类似问题

    呆呆敲代码的小Y
  • Python爬虫之打码平台的使用

    现在很多网站都会使用验证码来进行反爬,所以为了能够更好的获取数据,需要了解如何使用打码平台爬虫中的验证码

    海仔
  • iOS开发之XLForm的使用

    在iOS开发中,开发"表单"界面,字段稍微多一点的一般都用UITableView来做,而XLForm就是这样一个框架,它是创建动态表格视图最牛逼的iOS库, 用...

    YungFan
  • iOS开发之使用Git的基本使用(二)

    通过前文iOS开发之使用Git的基本使用(一)的学习,相信大家对如何将iOS项目通过Git传到GitHub账户上有了一个基本的了解,其过程是相对繁琐和容易出错的...

    YungFan
  • Unity与安卓交互 之 ✨ 在Android Studio中写代码导出aar包,在Unity中使用交互(小白完整篇)

    AndroidStudio中的操作步骤: 首先,打开AndroidStudio新建一个工程,版本不同,所以操作的界面跟步骤可能不太一样,但是核心就是包名罢了,...

    呆呆敲代码的小Y
  • 【Unity游戏开发】tolua之wrap文件的原理与使用

    本文内容转载自:https://www.cnblogs.com/blueberryzzz/p/9672342.html 。非常感谢原作者慷慨地授权转载,比心!...

    马三小伙儿
  • Unity 2019 打包黑屏、粉屏

    Mac Unity2019.2.9f1打包ios,用xcode发布到手机上粉色屏幕或者黑屏,并且错误日志可能为

    魔都路易斯
  • 深入理解JavaScript闭包之闭包的使用场景

    本篇文章是上一篇 深入理解JavaScript闭包之什么是闭包文章的下篇,闭包的使用场景。

    木子星兮
  • iOS开发之UIScrollView在Autolayout下的使用

    一、使用的基本原则: 原则1:UIScrollView的size依赖于subviews 首先在StoryBoard中拖入一个UIScrollView,用Pin按...

    YungFan
  • [602]app抓包之mitmproxy的安装和使用

    mitmproxy是一个支持HTTP和HTTPS的抓包程序,类似Fiddler、Charles的功能,只不过它通过控制台的形式操作。

    周小董
  • 从0开始学Golang之包的使用

    在使用Golang开发中,我们不可能把所有的项目文件都放在一个目录下面。这就需要根据实际的项目,将程序文件进行归类,不同的功能放在不同的目录。这就是包的作用之一...

    A梦多啦A
  • 使用charles无法抓取到iOS Simulator的请求数据包

    情况1:没有安装模拟器证书 解决:charles->Help -> Install Cahrles CA SSL Certificatein iOS Simul...

    梧雨北辰
  • Unity项目嵌入现有iOS项目的方法

    傅_hc
  • 使用Spring Boot Gradle 打war包的一点心得体会

    最近实验室的一些项目采用了Spring Boot架构进行开发。项目完成以后,就需要部署到服务器。常规办法就是将项目打成war包,直接拖到tomcat的webaa...

    算法与编程之美
  • iOS【 ASO项目使用的技术】之 Inter process Communication

    These are some of the existing methods to implement IPC on iOS:

    公众号iOS逆向
  • Unity3D入门

    Unity3D是一款很不错的游戏引擎,主要开发语言是C#、JavaScript,当然还有Boo,然后给我Unity3D的官网Url: Unity3D官网    ...

    SmileNicky
  • iOS开发之AFNetWorking初次使用会报错的坑

    第一次用 CocoPods 安装好了 AFNetWorking 后,无论使用 Get 还是 Post,总是直接进入 failure 的 block,错误信息如下...

    YungFan
  • iOS逆向之【截获分析网络数据包】 1、tcpdump 2、Wireshark的原理和基本使用 3、使用Charles联调测试

    抓取http/https的,推荐使用Charles;如果是socket,推荐使用Wireshark

    公众号iOS逆向
  • 【Unity 实用工具】✨| Unity 十款 浏览器相关插件 整理(web view / browser)

    有的是内嵌形式的,就是在Unity中显示浏览器的相关内容,有的则是会调用电脑本身的浏览器

    呆呆敲代码的小Y

扫码关注腾讯云开发者

领取腾讯云代金券