Unityから出力するXCodeプロジェクトにKeychain Groupを設定する

iOS向けアプリでキーチェーンを使用するときに、それをUnityから設定するための方法。
iCloudの設定を行うときも、entitlementsの登録をする必要があるので、昨今はそちらのほうが多いかもしれません。

XCodeEditorを使用します。
また、PostProcessBuildで作業することになると思うので、
対象のプロジェクトフォルダはpathToBuiltProject(相対パス)とします。

# entitlementsの生成

keychain情報が設定されている、entitlementsファイルを作成します。
中身はXMLファイルになっていて、こんな感じ。

```xml
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>keychain-access-groups</key>
    <array>
      <string>$(AppIdentifierPrefix)hogehogegroup</string>
    </array>
  </dict>
</plist>
```

entitlementsは基本的に、key要素が「キー」、その次にくる要素が「値」という形になっているよう。
keychain-access-groupsキーの次にくるarrayが、キーチェーングループとして登録されます。

これをC#で生成するには次のようにしていきます。

まず、空のplistとdict要素を持つ、XmlDocumentオブジェクトを生成します。

```csharp
XmlDocument document = new XmlDocument ();
XmlDocumentType doctype = document.CreateDocumentType ("plist", "-//Apple//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd", null);
document.AppendChild (doctype);

XmlElement plist = document.CreateElement ("plist");
plist.SetAttribute ("version", "1.0");
XmlElement dict = document.CreateElement ("dict");
plist.AppendChild (dict);
document.AppendChild (plist);
```
というようにします。

ここで注意する点は、

```csharp
document.CreateDocumentType ("plist", "-//Apple//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd", null);
```
で、最後にある内部サブセットの指定する引数にnullを指定すること。
これをnullに指定しないと、document.Save()で出力されるXMLが

```xml
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" []>
```
となります。
最後に「[]」がついているのがお分かりいただけるだろうか。
これがあると、XCodeはentitlementsを認識しない。厄介。

すでにentitlemetnsファイルを用意する場合は、document.Load()で読み込むことになるけれど、
このときそれをそのままSave()で出力しても、同様に「[]」がついてしまう。
なので、すでにあるentitlementsを読み込む場合は、

```csharp
XmlDocumentType oldType = document.DocumentType;
XmlDocumentType newType = document.CreateDocumentType (oldType.Name, oldType.PublicId, oldType.SystemId, null);
document.ReplaceChild (newType, oldType);
```
という形にして、DocumentTypeを作りなおしてやります。

この後は、普通に要素を追加していきます。

```csharp
XmlElement e = (XmlElement) document.SelectSingleNode ("/plist/dict");

XmlElement key = document.CreateElement ("key");
key.InnerText = "keychain-access-groups";
e.AppendChild (key);

XmlElement value = document.CreateElement ("array");
XmlElement value2 = document.CreateElement ("string");
value2.InnerText = "$(AppIdentifierPrefix)hogehogegroup";
value.AppendChild (value2);
e.AppendChild (value);
```

そして、出力。
出力先は、XCodeプロジェクトのパスの下に、Libraries/hogehoge.entitlementsというファイルを作ることにします。
```csharp
document.Save(pathToBuiltProject+"/Libraries/hogehoge.entitlements");
```

# entitlementsの登録

作成したentitlementsをプロジェクトに登録します。
entitlementsはひとつしか登録できないため、上書きすることになります。
もし他のAssetsなどと競合する場合は、そのAssetsが登録しているentitlementsを修正するなどして、登録自体は行わないようにするなどしてやる必要が出てきます。

## XCProjectオブジェクトの生成

XCodeのプロジェクトを操作するため、XCProjectオブジェクトを生成します。

```csharp
string absPathToBuiltProject = System.IO.Path.GetFullPath (pathToBuiltProject);
XCProject project = new XCProject (absPathToBuiltProject);
```
というように、pathToBuiltProjectをSystem.IO.Path.GetFullPath()で絶対パスに直して使用します。
これは後にファイルを追加するとき、ここで指定したパスからSystem.Uriオブジェクトを生成しているところがあるのですが、そこで相対パスがエラーとなるためです。

## entitlementsファイルをプロジェクトに登録

ファイルの登録は

```csharp
project.AddFile (absPathToBuiltProject+"/Libraries/hogehoge.entitlements", null, "SOURCE_ROOT", true, false);
```
というように行います。

## CODE_SIGN_ENTITLEMENTSの登録

各ビルド設定に、CODE_SIGN_ENTITLEMENTS要素を登録します。

```csharp
foreach (KeyValuePair<string, XCBuildConfiguration> item in project.buildConfigurations) {
	XCBuildConfiguration buildConfig = item.Value;
	buildConfig.buildSettings.Add ("CODE_SIGN_ENTITLEMENTS", "/Libraries/hogehoge.entitlements");
}
```

## プロジェクトの保存

最後にプロジェクトを保存します。

```csharp
project.Save();
```

結構雑な感じはしています。もっとうまいやり方があったら教えて下さい。