Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@mao_

【Unity】iOSネイティブプラグイン開発を完全に理解する - Xcodeの設定を自動化する

本編「【Unity】iOSネイティブプラグイン開発を完全に理解する」の付録記事です。
記事中での用語や略称についてはそのまま本編に倣う形で記載していきます。


Unityのビルド結果にあるプロジェクトファイル(.xcodeproj)を開き、Xcode上でターゲット1を選択してからBuild Settingsを開けば名前の通りにアプリケーションのビルドに関する設定を行えます。

例えば以下のスクリーンショットでは例として「Swiftのバージョン指定」を選択してます。

こちらの設定はXcode上から手動で変更する以外にも、Unity上からビルド毎に自動で設定を変更することも可能であり、今回はそのやり方について解説していきます。

スクリーンショット 2021-04-08 5.48.29.png

※ 前提としてUnityの場合には手動で変える運用はオススメできない

設定項目の変更状態はXcodeのプロジェクトファイル(.xcodeproj)に紐付かれることになりますが、Unityと言ったプロジェクトファイルを自動生成する様な環境だと、例えばReplace相応のビルドを行った際にはビルド設定が適用された.xcodeprojも消えてしまうため、都度手動で再設定を行う必要が出てきます。

手動設定は運用を踏まえると、ビルドの自動化との相性問題もあれば、そもそもヒューマンエラーに繋がる懸念も出てきます。
※逆にビルドエラー解決などの為に「一瞬手動で変えてみて試す」と言ったのは全然有りだと思います。ただ、最終的にはUnity上から自動で設定を適用するようにした方が良いかもです。

[PostProcessBuild]を利用して設定を自動化

Unityからプロジェクトファイルにある設定を書き換えるには、PostProcessBuildAttributeと言う属性を利用したEditor拡張を実装します。

こちらを利用するとビルドが完了したタイミングで属性を付けたメソッドを呼び出すことが出来るようになるため、ビルド完了後にXcodeプロジェクトファイルにアクセスして内容を書き換えると言ったアプローチを取ることが出来るようになります。

XcodePostProcess.cs
#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. 設定変更の適用
XcodePostProcess.cs
        /// <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に渡すことで初期化を完了させます。

XcodePostProcess.cs

        /// <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のパスに対して結果を書き込みます。
(先行して書いてますが、これ自体は一番最後のプロセスになります)

XcodePostProcess.cs
            // A. 変更結果で上書き
            File.WriteAllText(projectPath, project.WriteToString());

※この例ではReadFromStringWriteToStringで読み書きしましたが、他にも関連するAPIとしては以下のようなものが存在します。

■ B. 設定を適用するターゲットのGUIDを取得

Unity2019.3以降からはPBXProject.GetUnityFrameworkTargetGuidを実行してUnityFrameworkと言うターゲットのGUIDを持っておきます。
GUIDの用途については後述します。

XcodePostProcess.cs
            // B. 設定を適用するターゲットのGUIDを取得
            // 2019.3からは`UnityFramework`に分離しているので、targetGuidはこちらを指定する必要がある。
            var targetGuid = project.GetUnityFrameworkTargetGuid();

【補足】 そもそも"ターゲット"とは?

ターゲットとはXcodeプロジェクトにある「ビルド設定のまとまり」みたいなものであり、それぞれが独自の設定項目(アプリ名/ビルド設定/Bundle Identifier)などを持ちます。

Unityの場合には予め必要なものが設定されており、Xcodeの画面で言うと以下の①を選択したら表示される②の項目がターゲットになります。

スクリーンショット 2021-04-10 23.57.14.png

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を指定します
  • 第二引数にはビルドプロパティの名前を指定
  • 第三引数にはプロパティに設定したい値を指定
    • → 次で詳細に触れます
XcodePostProcess.cs
            // C. 設定変更の適用 (Swiftバージョンの設定)
            project.SetBuildProperty(targetGuid, "SWIFT_VERSION", "5.0");

ビルドプロパティの名前について

ここで指定するプロパティ名はXcode上から確認することが出来ます。

先ずはTARGETSからUnityFrameworkを選択し、[Build Settings]を開いたら設定したい項目を選択します。
(今回で言うとSwift Language Version)

選択したら以下のスクリーンショットの右上辺りにある①を選択してQuick Helpを表示し、②に記載されている文字列をUnity側で指定します。
→ この例で言うとSWUFT_VERSIONが該当する文字列

スクリーンショット 2021-04-11 1.14.43.png

他にもXcode Build Settingsと言うサイトには一通りのビルドプロパティや初期値、中には簡単な解説が記載されているので覚えておくと便利です。

プロパティに設定したい値について

引数に渡す値は文字列である必要があるために"5.0"を渡してます。

少し余談にはなりますが、もしValueに何を渡せばよいのか分からない場合には、プロジェクトファイルに含まれるproject.pbxprojをテキストエディタで開き、該当するビルドプロパティで検索してやることで設定値を割り出せたりします。

例えば"SWIFT_VERSION"で検索した場合には↓の様に5.0と言った値が設定されているので、Unity側で渡すパラメータもこちらに準拠させればOKです。

project.pbxproj
buildSettings = {
    (省略)
    SWIFT_VERSION = 5.0;
    (省略)
};

その他設定項目

Swiftのバージョン指定を例にビルド設定の変更方法について解説してきましたが、他にも以下のような項目も設定可能です。

  • Frameworkの追加
  • Info.plistの設定

これらの設定例については以下のサイトが参考になります。

設定の競合に注意

[PostProcessBuild]を利用した設定の自動化ですが、こちらの処理自体はプロジェクト中に複数実装することが出来ます。

もし複数箇所にてPBXProjectの書き換えを行った場合には、随時呼び出し順に従って設定が書き換えられていく事になりますが...注意しないと同一設定の書き換えが発生して競合するなんてことが起こり得ます。

同一設定は後から書き換えられたほうが優先される

例えば以下のXcodePostProcess1.csXcodePostProcess2.csと言うソースがあったとして、先に呼び出される想定のXcodePostProcess1.csではSWIFT_VERSION5.0に書き換えるとします。

そして次に呼び出される想定のXcodePostProcess2.csにてSWIFT_VERSION4.0に書き換えたとしたら、最終的なビルド結果は「SWIFT_VERSION : 4.0」が適用されることになります。

XcodePostProcess1.cs (クリックで展開)
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 (クリックで展開)
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のこの設定を書き換えてください」と明記するなりした方が良いかもしれません。


  1. ターゲットについては後述します 

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What is going on with this article?