之前已经写过一篇关于XCode配置的,但那篇其实还有一个小bug,就是开启不了有些Capability,需要自己手动开启,这其实很繁琐,需要复制粘贴文本等。
刚开始怀疑是不是Unity版本的问题,因为用的是Unity官方自带的API来添加Capability。之前的Unity版本是2017.4.7f1,最近项目升级了Unity版本,现在是2017.4.22f1版本,但测试了,还是开启不了有些Capability。
//project.AddCapability(target, PBXCapabilityType.PushNotifications);
project.AddCapability(target, PBXCapabilityType.InAppPurchase);
project.AddCapability(target, PBXCapabilityType.BackgroundModes);
注释掉的代码测试过,开启不了PushNotifications,下面的2个能给开启。但也有问题,看下图:
也用过ProjectCapabilityManager来尝试开启Capability,发现没有用,倒是能修复BackgroudModes的问题。网上找了一堆文章,发现都没有好的解决方案。最后不甘心的去官网下载了源码来看,官网地址:https://bitbucket.org/Unity-Technologies/xcodeapi/src。最后终于解决一键生成XCode工程的问题,虽然还剩下一个问题,这估计不好解决。
看了ProjectCapabilityManager源码后,大致清楚开启Capability需要修改3个地方,一个是project.pbxproj,一个是info.plist,一个则是entitlements。
举个例子BackgroudModes的问题,其实只要修改info.plist,就可以开启。代码如下:
/// <summary>
/// 处理Plist工程
/// </summary>
/// <param name="project"></param>
/// <param name="plistPath"></param>
private static void GeneratePlistFile(PlistElementDict rootDict, string plistPath)
{
PlistElementArray urlArray = null;
//Add BackgroundModes
if (!rootDict.values.ContainsKey("UIBackgroundModes"))
urlArray = rootDict.CreateArray("UIBackgroundModes");
else
urlArray = rootDict.values["UIBackgroundModes"].AsArray();
urlArray.values.Clear();
urlArray.AddString("remote-notification");
}
加不加这句代码都没问题,已测试:
project.AddCapability(target, PBXCapabilityType.BackgroundModes);
entitlements的用法则是动态生成一个entitlements文件,举个例子,添加KeychainSharing:
string key_KeychainSharing = "keychain-access-groups";
var arr = (tempEntitlements.root[key_KeychainSharing] = new PlistElementArray()) as PlistElementArray;
arr.values.Add(new PlistElementString("$(AppIdentifierPrefix)com.tencent.lycqsh"));
arr.values.Add(new PlistElementString("$(AppIdentifierPrefix)com.tencent.wsj.keystoregroup"));
最重要的是entitlements的路径不要写错,是相对路径,我之前写成绝对路径,找这个问题,找了半天,这个路径也是有问题的,要去掉最前面的/,路径应该Unity-iPhone/test.entitlements或者test.entitlements。
最终项目使用的代码:
private static void AddCapability(PBXProject project, string pathToBuiltProject)
{
string target = project.TargetGuidByName(PBXProject.GetUnityTargetName());
// Add BackgroundModes And Need to modify info.plist
project.AddCapability(target, PBXCapabilityType.BackgroundModes);
project.AddCapability(target, PBXCapabilityType.InAppPurchase);
// Need Create entitlements
string relativeEntitlementFilePath = "Unity-iPhone/xxxx.entitlements";
string absoluteEntitlementFilePath = pathToBuiltProject + "/" + relativeEntitlementFilePath;
PlistDocument tempEntitlements = new PlistDocument();
string key_KeychainSharing = "keychain-access-groups";
var arr = (tempEntitlements.root[key_KeychainSharing] = new PlistElementArray()) as PlistElementArray;
arr.values.Add(new PlistElementString("$(AppIdentifierPrefix)com.tencent.xxxx"));
arr.values.Add(new PlistElementString("$(AppIdentifierPrefix)com.tencent.wsj.keystoregroup"));
string key_PushNotifications = "aps-environment";
tempEntitlements.root[key_PushNotifications] = new PlistElementString("production");
project.AddCapability(target, PBXCapabilityType.PushNotifications, relativeEntitlementFilePath);
project.AddCapability(target, PBXCapabilityType.KeychainSharing, relativeEntitlementFilePath);
string projPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);
File.WriteAllText(projPath, project.WriteToString());
tempEntitlements.WriteToFile(absoluteEntitlementFilePath);
ModifyEntitlementFile(absoluteEntitlementFilePath);
}
private static void ModifyEntitlementFile(string absoluteEntitlementFilePath)
{
if (!File.Exists(absoluteEntitlementFilePath)) return;
try
{
StreamReader reader = new StreamReader(absoluteEntitlementFilePath);
var content = reader.ReadToEnd().Trim();
reader.Close();
var needFindString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
var changeString = "<?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\">";
Debug.Log("Before: " + content);
content = content.Replace(needFindString, changeString);
Debug.Log("After: " + content);
StreamWriter writer = new StreamWriter(new FileStream(absoluteEntitlementFilePath, FileMode.Create));
writer.WriteLine(content);
writer.Flush();
writer.Close();
}
catch (Exception e)
{
Debug.Log("ModifyEntitlementFile - Failed: " + e.Message);
}
}
这里还有一个问题。就是PushNotifications的问题,查到了这好像是官方Unity5.6版本就有的问题,会导致如下的结果:
再手动关闭和开启PushNotifications,会发现其他的也生效了,比如KeychainSharing:
这一步操作,通过SVN比较发现变化了2个文件,上面代码中有一个接口(ModifyEntitlementFile)就是按照这种格式进行修改,发现没有用,估计是另一个文件产生作用,但因为打开是乱码,非常不好处理。
虽然没有达到预期的效果,但也不错,省去了拷贝字符串和开启其他Capability的工作,也是一种简化。希望有大神知道还可以再怎么优化。
ProjectCapabilityManager核心代码:
/// <summary>
/// Creates a new instance of ProjectCapabilityManager. The returned
/// instance assumes ownership of the referenced pbxproj project file,
/// the entitlements file and project Info.plist files until the last
/// WriteToFile() call.
/// </summary>
/// <param name="pbxProjectPath">Path to the pbxproj file.</param>
/// <param name="entitlementFilePath">Path to the entitlements file.</param>
/// <param name="targetName">The name of the target to add entitlements for.</param>
public ProjectCapabilityManager(string pbxProjectPath, string entitlementFilePath, string targetName)
{
m_BuildPath = Directory.GetParent(Path.GetDirectoryName(pbxProjectPath)).FullName;
m_EntitlementFilePath = entitlementFilePath;
m_PBXProjectPath = pbxProjectPath;
project = new PBXProject();
project.ReadFromString(File.ReadAllText(m_PBXProjectPath));
m_TargetGuid = project.TargetGuidByName(targetName);
}
/// <summary>
/// Writes the modifications to the project file, entitlements file and
/// the Info.plist file. Any external changes to these files after
/// the ProjectCapabilityManager instance has been created and before
/// the call to WriteToFile() will be overwritten.
/// </summary>
public void WriteToFile()
{
File.WriteAllText(m_PBXProjectPath, project.WriteToString());
if (m_Entitlements != null)
m_Entitlements.WriteToFile(PBXPath.Combine(m_BuildPath, m_EntitlementFilePath));
if (m_InfoPlist != null)
m_InfoPlist.WriteToFile(PBXPath.Combine(m_BuildPath, "Info.plist"));
}
private PlistDocument GetOrCreateEntitlementDoc()
{
if (m_Entitlements == null)
{
m_Entitlements = new PlistDocument();
string[] entitlementsFiles = Directory.GetFiles(m_BuildPath, m_EntitlementFilePath);
if (entitlementsFiles.Length > 0)
{
m_Entitlements.ReadFromFile(entitlementsFiles[0]);
}
else
{
m_Entitlements.Create();
}
}
return m_Entitlements;
}
更新:最后一个问题也解决了,PushNotifications的字段不要写production,改为development,就解决了,昨天好像是SVN更新后,代码没有生效,就没有测试到改成development的结果,就误以为无效。
string key_PushNotifications = "aps-environment";
tempEntitlements.root[key_PushNotifications] = new PlistElementString("development");