最近在工作中遇到了一些集成 SDK 的问题,由于整个框架都是自动化的,所以 SDK 得集成也需要把所需的编译环境和启动代码写在 Unity 的脚本中。这两天也查找了很多资料,最终完成了集成,在这里记录一下过程和重点。

主要使用的方法

public class iOSPostProcessBuild
{
	//该属性是在build完成后,被调用执行的
	[PostProcessBuild]
	public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuildProject)
	{
		if (buildTarget != BuildTarget.iOS)
			return;
		//初始化
		string projectPath = pathToBuildProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
		PBXProject pbxProj = new PBXProject();
		pbxProj.ReadFromFile(projectPath);
		string targetGuid = pbxProj.TargetGuidByName("Unity-iPhone");
		...
		//保存所有修改
		pbxProject.WriteToFile(projectPath);
	}
}

[PostProcessBuild] 下的方法会在build完成之后被调用,在这个方法对 XCode 工程的编译选项和 plist 文件进行编辑。
对 XCode 的编译选项进行操作
需要引入 UnityEditor.iOS.Xcode 命名空间。

//添加flag
pbxProj.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC");
//关闭BitCode
pbxProj.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
//添加framwork
pbxProj.AddFrameworkToProject(targetGuid, "CFNetwork.framework", false);
//添加lib
pbxProj.AddFileTobuild(targetGuid, pbxProj.AddFile("usr/lib/libsqlite3.tbd", "Frameworks/libsqlite3.tbd", PBXSourceTree.Sdk));
//应用修改
File.WriteAllText(projectPath, pbxProj.WriteToString());

修改Info.plist文件

string plistPath = Path.Combine(pathToBuildProject, "Info.plist");
PlistDocument plist = new PlistDocument();
plist.ReadFromFile(plistPath);
PlistElementDict rootDict = plist.root;
// NSAppTransportSecurity
const string nsappKey = "NSAppTransportSecurity";
PlistElementDict nsappDic;
PlistElement nspel;
if (rootDict.values.TryGetValue(nsappKey, out nspel))
	nsappDic = nspel.AsDict();
else
	nsappDic = rootDict.CreateDict(nsappKey);
nsappDic.SetBoolean("NSAllowsArbitraryLoads", true);
// 应用修改
File.WriteAllText(plistPath, plist.WriteToString());

修改OC代码

namespace XCodeBuilder.XCodeEditor
{
	public partial class XCodeClass : System.IDisposable
	{
		private string filePath;
		public XCodeClass(string fPath)
		{
			filePath = fPath;
			if (!System.IO.File.Exists(filePath))
			{
				Debug.LogError(filePath + "路径下文件不存在");
				return;
			}
		}
		public void WriteBelow(string below, string text)
		{
			StreamReader sr = new StreamReader(filePath);
			string text_all = sr.ReadToEnd();
			sr.Close();
			int beginIndex = text_all.IndexOf(below);
			if (beginIndex == -1)
			{
				Debug.LogError(filePath + "中没有找到标致" + below);
				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 sw = new StreamWriter(filePath);
			sw.Write(text_all);
			sw.Close();
		}
		public void Replace(string below, string newText)
		{
			StreamReader sr = new StreamReader(filePath);
			string text_all = sr.ReadToEnd();
			sr.Close();
			int beginIndex = text_all.IndexOf(below);
			if (beginIndex == -1)
			{
				Debug.LogError(filePath + "中没有找到标致" + below);
				return;
			}
			text_all = text_all.Replace(below, newText);
			StreamWriter sw = new StreamWriter(filePath);
			sw.Write(text_all);
			sw.Close();
		}
		public void Dispose()
		{
		}
	}
}
//插入代码
//读取UnityAppController.mm文件
string unityAppControllerPath = pathToBuildProject + "/Classes/UnityAppController.mm";
XCodeClass UnityAppController = new XCodeClass(unityAppControllerPath);
//在指定代码后面增加一行代码
UnityAppController.WriteBelow("指定代码", "需要添加的代码");
//在指定代码后面增加一大行代码
string newCode = "需\n" +
	"要\n" +
	"添\n" +
	"加\n" +
	"的\n" +
	"大段代码。";
UnityAppController.WriteBelow("指定代码", newCode);

添加自定义类库

//将库文件从Assets中拷贝到XCode工程中
public static void CopyDirectory(string srcPath, string dstPath, string[] excludeExtensions, bool overwrite = true)
{
	if (!Directory.Exists(dstPath))
		Directory.CreateDirectory(dstPath);
	foreach (var file in Directory.GetFiles(srcPath, "*.*", SearchOption.TopDirectoryOnly))
	{
		File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file)), overwrite);
	}
	foreach (var dir in Directory.GetDirectories(srcPath))
		CopyDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir)), excludeExtensions, overwrite);
}
#region 添加资源文件(中文路径 会导致 project.pbxproj 解析失败)
string frameworksPath = Application.dataPath + "库文件所在的文件夹";
string[] directories = Directory.GetDirectories(frameworksPath, "*", SearchOption.TopDirectoryOnly);
for (int i = 0; i < directories.Length; i++)
{
	string path = directories[i];
	string name = path.Replace(frameworksPath + "/", "");
	string destDirName = pathToBuildProject + "目标路径" + name;
	if (Directory.Exists(destDirName))
		Directory.Delete(destDirName, true);
	Debug.Log(path + " => " + destDirName);
	CopyDirectory(path, destDirName, new string[] { ".meta", ".framework", ".bundle", ".mm", ".c", ".m", ".h", ".xib", ".a", ".plist", ".org", "" }, false);
	foreach (string file in Directory.GetFiles(destDirName, "*.*", SearchOption.AllDirectories))
	{
		pbxProject.AddFileToBuild(targetGuid, pbxProject.AddFile(destDirName, "目标路径" + name, PBXSourceTree.Sdk));
		pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)" + "目标路径");
		pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(SRCROOT)" + "目标路径");
		pbxProject.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)" + "目标路径");
	}
}
#endregion