本編「【Unity】iOSネイティブプラグイン開発を完全に理解する」の付録記事です。
記事中での用語や略称についてはそのまま本編に倣う形で記載していきます。
Unityのビルド結果にあるプロジェクトファイル(.xcodeproj
)を開き、Xcode上でターゲット1を選択してからBuild Settings
を開けば名前の通りにアプリケーションのビルドに関する設定を行えます。
例えば以下のスクリーンショットでは例として「Swiftのバージョン指定」を選択してます。
こちらの設定はXcode上から手動で変更する以外にも、Unity上からビルド毎に自動で設定を変更することも可能であり、今回はそのやり方について解説していきます。
※ 前提としてUnityの場合には手動で変える運用はオススメできない
設定項目の変更状態はXcodeのプロジェクトファイル(.xcodeproj
)に紐付かれることになりますが、Unityと言ったプロジェクトファイルを自動生成する様な環境だと、例えばReplace
相応のビルドを行った際にはビルド設定が適用された.xcodeproj
も消えてしまうため、都度手動で再設定を行う必要が出てきます。
手動設定は運用を踏まえると、ビルドの自動化との相性問題もあれば、そもそもヒューマンエラーに繋がる懸念も出てきます。
※逆にビルドエラー解決などの為に「一瞬手動で変えてみて試す」と言ったのは全然有りだと思います。ただ、最終的にはUnity上から自動で設定を適用するようにした方が良いかもです。
[PostProcessBuild]
を利用して設定を自動化
Unityからプロジェクトファイルにある設定を書き換えるには、PostProcessBuildAttributeと言う属性を利用したEditor拡張を実装します。
こちらを利用すると**ビルドが完了したタイミングで属性を付けたメソッドを呼び出すことが出来るようになるため、**ビルド完了後にXcodeプロジェクトファイルにアクセスして内容を書き換えると言ったアプローチを取ることが出来るようになります。
#if UNITY_IOS
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
namespace MinimumExample.Editor
{
static class XcodePostProcess
{
/// <summary>
/// ビルド完了後に呼び出される
/// </summary>
/// <param name="target">ビルドターゲット</param>
/// <param name="path">ビルド結果のパス</param>
[PostProcessBuild]
static void OnPostProcessBuild(BuildTarget target, string path)
{
// ビルドターゲットがiOSなら処理
if (target != BuildTarget.iOS) return;
// TODO: ここでビルド結果に対して手を加えることが出来る
}
}
}
#endif
PBXProject
を利用してXcodeのプロジェクトファイルを書き換える
Unityからプロジェクトファイルの内容を書き換えるにはPBXProjectと言うiOS(と言うよりはXcode)向けのAPIを利用します。
簡単な例として「Swiftバージョンを5.0に設定する」例を取り上げつつ、以下の要点について順に解説していきます。
- A. ビルド結果のパスから
PBXProject
を読み込んで、終わったら変更結果で上書き - B. 設定を適用するターゲットのGUIDを取得
- C. 設定変更の適用
/// <summary>
/// Swiftを実装するにあたって必要な設定を自動で適用する
/// </summary>
/// <param name="target">ビルドターゲット</param>
/// <param name="path">ビルド結果のパス</param>
[PostProcessBuild]
static void OnPostProcessBuild(BuildTarget target, string path)
{
if (target != BuildTarget.iOS) return;
// A. ビルド結果のパスからPBXProjectを読み込み
var projectPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromString(File.ReadAllText(projectPath));
// B. 設定を適用するターゲットのGUIDを取得
// 2019.3からは`UnityFramework`に分離しているので、targetGuidはこちらを指定する必要がある。
var targetGuid = project.GetUnityFrameworkTargetGuid();
// C. 設定変更の適用 (Swiftバージョンの設定)
project.SetBuildProperty(targetGuid, "SWIFT_VERSION", "5.0");
// A. 変更結果で上書き
File.WriteAllText(projectPath, project.WriteToString());
}
【補足】 そもそもPBXProject
とは?
PBXProject
はXcodeプロジェクトファイルの中に含まれる設定ファイル的なものであり、主にプロジェクト内にあるファイルの定義/フォルダ階層構造/ビルド設定と言った要素を持ちます。
ビルド結果にある.xcodeproj
を選択したら「右クリック -> パッケージの内容を表示」を選択することで、Finder上からアクセスすることが出来ます。(以下にあるproject.pbxproj
が該当)
[XXX.xcodeproj]
L project.pbxproj
L project.wxworkspace
L [xcshareddata]
ちなみにproject.pbxproj
自体はテキスト形式なのでテキストエディタで開いて編集することも可能です。
■ A. ビルド結果のパスからPBXProject
を読み込んで、終わったら変更結果で上書き
PostProcessBuild
から呼び出されるメソッドはパラメータとして「ビルド結果のパス」を受け取ることが出来るので、こちらをPBXProject.GetPBXProjectPathに渡すことで実際のPBXProject
のパスを取得します。
あとは取得したパスからPBXProject
を文字列として読み込み、それをReadFromStringに渡すことで初期化を完了させます。
/// <param name="target">ビルドターゲット</param>
/// <param name="path">ビルド結果のパス</param>
[PostProcessBuild]
static void OnPostProcessBuild(BuildTarget target, string path)
{
if (target != BuildTarget.iOS) return;
// A. ビルド結果のパスからPBXProjectを読み込み
var projectPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromString(File.ReadAllText(projectPath));
そして後述する諸々の設定の適用が完了したら、上記で取得したPBXProject
のパスに対して結果を書き込みます。
(先行して書いてますが、これ自体は一番最後のプロセスになります)
// A. 変更結果で上書き
File.WriteAllText(projectPath, project.WriteToString());
※この例ではReadFromString
とWriteToString
で読み書きしましたが、他にも関連するAPIとしては以下のようなものが存在します。
■ B. 設定を適用するターゲットのGUIDを取得
Unity2019.3以降からはPBXProject.GetUnityFrameworkTargetGuidを実行して**UnityFramework
と言うターゲットのGUID**を持っておきます。
GUIDの用途については後述します。
// B. 設定を適用するターゲットのGUIDを取得
// 2019.3からは`UnityFramework`に分離しているので、targetGuidはこちらを指定する必要がある。
var targetGuid = project.GetUnityFrameworkTargetGuid();
【補足】 そもそも"ターゲット"とは?
ターゲットとはXcodeプロジェクトにある「ビルド設定のまとまり」みたいなものであり、それぞれが独自の設定項目(アプリ名/ビルド設定/Bundle Identifier)などを持ちます。
Unityの場合には予め必要なものが設定されており、Xcodeの画面で言うと以下の①を選択したら表示される②の項目がターゲットになります。
2019.3からはアプリ本体側の機能がUnityFramework
に集約されているので、PBXProject.GetUnityFrameworkTargetGuidからこのUnityFramework
のGUIDを取得する必要があります。
もっと具体的に「それぞれのターゲットに何がどう含まれているのか?」については以下の公式ドキュメントのProject Targets
を御覧ください。
【補足】 Unity2019.3前後ではプラグインに関する設定が変わってくるので注意
Unity 2019.3
から新機能として**「Unity as a Library」**が入った影響で、iOS向けビルド後のXcodeプロジェクトファイル(.xcodeproj
)の構成に大きく変更が掛かりました。
この記事中では2019.4をターゲットとしているので、基本的には2019.3以降の書き方で解説をしてますが、2018系統などを利用する場合にはこれらの設定が変わってくることについては念頭に置いておく必要があります。
詳細は以下の記事にて纏めているので、こちらを御覧ください。
■ C. 設定変更の適用
残るは設定変更の適用です。
今回の題材である「Swiftバージョンを5.0に設定する」の場合には、PBXProject.SetBuildPropertyを利用します。
- 第一引数には
ターゲットのGUID
を指定- →
B.
の項目で取得したUnityFramework
のGUIDを指定します
- →
- 第二引数には
ビルドプロパティの名前
を指定 - 第三引数には
プロパティに設定したい値
を指定- → 次で詳細に触れます
// C. 設定変更の適用 (Swiftバージョンの設定)
project.SetBuildProperty(targetGuid, "SWIFT_VERSION", "5.0");
ビルドプロパティの名前について
ここで指定するプロパティ名はXcode上から確認することが出来ます。
先ずはTARGETSからUnityFramework
を選択し、[Build Settings]を開いたら設定したい項目を選択します。
(今回で言うとSwift Language Version
)
選択したら以下のスクリーンショットの右上辺りにある①を選択してQuick Help
を表示し、②に記載されている文字列をUnity側で指定します。
→ この例で言うとSWUFT_VERSION
が該当する文字列
他にもXcode Build Settingsと言うサイトには一通りのビルドプロパティや初期値、中には簡単な解説が記載されているので覚えておくと便利です。
プロパティに設定したい値について
引数に渡す値は文字列である必要があるために"5.0"
を渡してます。
少し余談にはなりますが、もしValueに何を渡せばよいのか分からない場合には、プロジェクトファイルに含まれるproject.pbxproj
をテキストエディタで開き、該当するビルドプロパティで検索してやることで設定値を割り出せたりします。
例えば"SWIFT_VERSION"
で検索した場合には↓の様に5.0
と言った値が設定されているので、Unity側で渡すパラメータもこちらに準拠させればOKです。
buildSettings = {
(省略)
SWIFT_VERSION = 5.0;
(省略)
};
その他設定項目
Swiftのバージョン指定を例にビルド設定の変更方法について解説してきましたが、他にも以下のような項目も設定可能です。
-
Framework
の追加 -
Info.plist
の設定
これらの設定例については以下のサイトが参考になります。
設定の競合に注意
[PostProcessBuild]
を利用した設定の自動化ですが、こちらの処理自体はプロジェクト中に複数実装することが出来ます。
もし複数箇所にてPBXProject
の書き換えを行った場合には、随時呼び出し順に従って設定が書き換えられていく事になりますが...注意しないと同一設定の書き換えが発生して競合するなんてことが起こり得ます。
同一設定は後から書き換えられたほうが優先される
例えば以下のXcodePostProcess1.cs
とXcodePostProcess2.cs
と言うソースがあったとして、先に呼び出される想定のXcodePostProcess1.cs
では**「SWIFT_VERSION
を5.0
」**に書き換えるとします。
そして次に呼び出される想定のXcodePostProcess2.cs
にて**「SWIFT_VERSION
を4.0
」**に書き換えたとしたら、最終的なビルド結果は「SWIFT_VERSION : 4.0」が適用されることになります。
**`XcodePostProcess1.cs` (クリックで展開)**
// NOTE: 属性の引数に呼び出し順を指定可能
[PostProcessBuild(1)]
static void OnPostProcessBuild(BuildTarget target, string path)
{
if (target != BuildTarget.iOS) return;
var projectPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromString(File.ReadAllText(projectPath));
var targetGuid = project.GetUnityFrameworkTargetGuid();
// SWIFT_VERSIONを`5.0`に書き換え
project.SetBuildProperty(targetGuid, "SWIFT_VERSION", "5.0");
File.WriteAllText(projectPath, project.WriteToString());
}
**`XcodePostProcess2.cs` (クリックで展開)**
[PostProcessBuild(2)]
static void OnPostProcessBuild(BuildTarget target, string path)
{
if (target != BuildTarget.iOS) return;
var projectPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromString(File.ReadAllText(projectPath));
var targetGuid = project.GetUnityFrameworkTargetGuid();
// SWIFT_VERSIONを`4.0`に書き換え
project.SetBuildProperty(targetGuid, "SWIFT_VERSION", "4.0");
File.WriteAllText(projectPath, project.WriteToString());
}
汎用的なプラグインを実装する際には、書き換えに注意すること
もし色んなプロジェクトで使える想定の汎用的なプラグインを実装するとしたら、上記のような性質があるので**PBXProject
の書き換えには注意する必要があります。**
(「導入したプラグイン側で意図せずにPBXProject
を書き換えてしまった」、若しくは「導入先のプロジェクトで期待する設定が書き換えられてしまった」なんてことが起こり得るので)
もしプラグインの都合でPBXProject
を書き換える必要性が出てきた場合には、README辺りに「PBXProject
のこの設定を書き換えます」 or 「導入するならPBXProject
のこの設定を書き換えてください」と明記するなりした方が良いかもしれません。
-
ターゲット
については後述します ↩