Unity导出Xcode后自动配置

本文中工程地址:https://github.com/JinLiGame/SDK-Demo/tree/master/IOS/EfunSDKDemo

为什么需要自动配置?

总所周知,unity打包ios的流程是:先由unity工程导出xcode工程,再由xcode工程导出ios。这里导出的xcode工程的一些配置unity已经帮我们配置好了,比如包名,产品名等。但是我们导入的第三方sdk需要的一些权限或者库,unity是不会帮我们配置的,就需要我们手动配置。

自动配置原理

xcode工程会有.xcodeproj文件,而unity在导出xcode工程后,提供了修改.xcodeproj文件的apiPostProcessBuild标签。确切来说,是修改.xcodeproj文件下的.pbxproj文件。

PostProcessBuild标签

unity导出xcode工程后会自动调用PostProcessBuild标签对应的方法,PostProcessBuild标签中的参数就是该方法调用的顺序。

怎样使用?

首先找到.pbxproj文件,这个文件很隐蔽,右键.xcodeproj,选择显示包内容,就能找到.pbxproj文件啦。
pbxproj01
这个文件存储了xcode中配置的各种参数,所谓的自动配置就是我们在导出xcode工程后,通过unity来修改这些参数,从而达到无需手动配置的目的。

创建脚本

接下来就是PostProcessBuild标签,我们新建一个脚本,放在Editor目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//由于UnityEditor.iOS.Xcode和UnityEditor.iOS.Xcode.Extensions这两个命名空间只有在macOS上存在,所以加个宏
#if UNITY_IOS

using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using UnityEditor.iOS.Xcode.Extensions;
using UnityEngine;

public class XcodeProjectSetting
{
[PostProcessBuild(100)]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{

}
}
}

#endif

获取PBXProject文件

1
2
3
string projPath = PBXProject.GetPBXProjectPath(path);
PBXProject xcodeProj = new PBXProject();
xcodeProj.ReadFromString(File.ReadAllText(projPath));

获取当前项目

1
var targetName = xcodeProj.TargetGuidByName(PBXProject.GetUnityTargetName());

配置证书相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//关闭自动证书管理
xcodeProj.SetBuildProperty(targetName, "CODE_SIGN_STYLE", "Manual");
// 修改Code Signing Entitlements为上级目录的EfunSDKEntitlements.entitlements文件
xcodeProj.SetBuildProperty(targetName, "CODE_SIGN_ENTITLEMENTS", "../EfunSDKEntitlements.entitlements");
// 修改Provisioning Profile
var provision = Debug.isDebugBuild ? "devjpakdy20190830" : "adhocjpakdy20190830";
xcodeProj.SetBuildProperty(targetName, "PROVISIONING_PROFILE_SPECIFIER", provision);
// 修改Development Team
xcodeProj.SetBuildProperty(targetName, "DEVELOPMENT_TEAM", "SWQL42468W");
// 修改Code Signing Identity
var codeSignIdentity = Debug.isDebugBuild ?
"iPhone Developer: Alex Shen (VZ46PPHW64)"
: "iPhone Distribution: Efun Japan Ltd. (SWQL42468W)";
xcodeProj.SetBuildProperty(targetName, "CODE_SIGN_IDENTITY", codeSignIdentity);
xcodeProj.SetBuildProperty(targetName, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", codeSignIdentity);

Info.plist文件配置

本文中对Info.plist文件的配置并不是修改Info.plist的内容,而是修改了xcode工程Info.plist的引用,我们把已经配置好的Info.plist文件放于xcode工程的同级目录,并且引用这个已经配置好的Info.plist文件,同时删除xcode工程中没有用到的Info.plsit文件。

1
2
3
4
5
6
7
8
9
10
11
xcodeProj.SetBuildProperty(targetName, "INFOPLIST_FILE", "../Info.plist");
if (xcodeProj.ContainsFileByProjectPath("Info.plist"))
{
var fileGuid = xcodeProj.FindFileGuidByProjectPath("Info.plist");
xcodeProj.RemoveFile(fileGuid);
}
string filePath = Path.Combine(path, "Info.plist");
if (File.Exists(filePath))
{
File.Delete(filePath);
}

设置Enable Bitcode

1
xcodeProj.SetBuildProperty(targetName, "ENABLE_BITCODE", "NO");

添加Other Linker Flags

1
xcodeProj.AddBuildProperty(targetName, "OTHER_LDFLAGS", "-ObjC");
1
xcodeProj.AddFrameworkToProject(targetName, "AddressBook.framework", false);

添加Embed Frameworks

1
2
3
4
5
string fwPath = Path.Combine(prePath, "EfunSDK/efn_ios_jp_2.0.0/Twitter/TwitterKit.framework");
string fw = xcodeProj.AddFile(fwPath, "Frameworks/" + fwPath, PBXSourceTree.Sdk);
PBXProjectExtensions.AddFileToEmbedFrameworks(xcodeProj, targetName, fw);
// 如果没有这句话,运行会崩溃,报 image not found 错误
xcodeProj.SetBuildProperty(targetName, "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks");

设置搜索路径

1
2
3
xcodeProj.AddBuildProperty(targetName, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/../EfunSDK/**");
xcodeProj.AddBuildProperty(targetName, "HEADER_SEARCH_PATHS", "$(SRCROOT)/../EfunSDK/**");
xcodeProj.AddBuildProperty(targetName, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)/../EfunSDK/**");

最后,写入PBXProject文件

1
File.WriteAllText(projPath, xcodeProj.WriteToString());

后续改进

本文中第三方SDK没有放于unity工程的Plugins/IOS中,而是放在了xcode工程的同级目录,在xcode工程中引入这个第三方SDK,这样的优点就是能够减少unity导出xcode工程的时间。本文中的工程目录结构图如下:
pbxproj02

但是,这样还有一个小问题就是,由于目前没有找到通过unity将第三方SDK作为group自动配置到xcode工程中的方法,只能在导出xcode工程后手动的引入第三方SDK。