Xcode設定自動化スクリプト

## PBXProjectEditor.cs
```csharp:PBXProjectEditor.cs
#if UNITY_IOS
/**
 * @file     PBXProjectEditor.cs
 * @brief    PBXプロジェクトの編集を行う。
 * @date     $Date$
 * @version  $Rev$
 * @author   $Author$
 */
using UnityEditor;
using UnityEditor.iOS.Xcode;
using UnityEditor.iOS.Xcode.Extensions;
using System;
using System.IO;
using System.Collections.Generic;

namespace yyama2.Editor.XCProjEdit
{

    /// <summary>
    /// PBXプロジェクトのプロパティを設定する。
    /// 
    /// 
    /// 設定は下記の様に定義してください。
    /// 
    /// ■file操作（追加・削除・移動） ※PBXParamFile.FileParam参照
    /// new List() {
    ///     new PBXParamFile.FileParam( 
    ///         type, 
    ///         fromDir, 
    ///         fromFile, 
    ///         toDir, 
    ///         toFile, 
    ///         compileFlag // 省略可
    ///     ),
    /// }
    /// 
    /// ■Framework ※PBXParamFramework参照
    /// new Dictionary<string, int>() {
    ///     { "value.framework", 1 },
    /// } 
    /// 
    /// ■Property設定 ※PBXParamProperty.PropertyParams参照
    /// new Dictionary<string, List<PBXParamProperty.PropertyParams>>() { 
    ///     "property", 
    ///     new List<PBXParamProperty.PropertyParams>() {
    ///         new PBXParamProperty.PropertyParams( 
    ///             configType:"config_type",          // configType
    ///             addValue:new string[]{ "YES" },  // AddValue
    ///             nremoveType:ew string[]{ "NO" },   // RemoveValue
    ///             forceOverride:false                   // 上書きフラグ
    ///         ), 
    ///     }
    /// },
    /// </summary>
    public static class PBXProjectEditor
    {

        /// <summary> Unityデフォルトのプロジェクト名（基本変更しなくていい）</summary>
        public const string NATIVE_TARGET_NAME = "Unity-iPhone";
        /// <summary> XcodeProjectの拡張子</summary>
        public const string XCODE_PROJ_FILE_EXTENSION = ".xcodeproj";
        /// <summary> XcodeProjectファイル</summary>
        public const string XCODE_PROJ_FILE = NATIVE_TARGET_NAME + XCODE_PROJ_FILE_EXTENSION;
        /// <summary> PBXプロジェクトファイル</summary>
        public const string PBX_PROJ_FILE = "project.pbxproj";

        /// <summary> project.pbxprojのパスを取得する</summary>
        /// <param name="path"> project.pbxprojのあるフォルダパス</param>
        /// <returns> project.pbxprojのファイルパス</returns>
        public static string GetDefaultPBXProjectPath(string path)
        {
            return Path.Combine(Path.Combine(path, XCODE_PROJ_FILE), PBX_PROJ_FILE);
        }

        /// <summary>
        /// PBXプロジェクトの編集
        /// </summary>
        /// <param name="pbxProjPath">Pbx proj path.</param>
        /// <param name="pbxParams">Pbx parameters.</param>
        /// <param name="callClass">この関数を呼び出しているクラスタイプ.ログ出力に飲み使用します.typeof( クラス )</param>
        public static void PBXProjectEdit(string pbxProjPath, List<PBXParam> pbxParams, Type callClass = null, bool isMain = true)
        {
            string className = "";

            if (callClass != null)
            {
                className += callClass.Name;
            }
            UnityEngine.Debug.Log("PBXProjectEditor:" + className);
            string log = "======================= PBXProjectValue =======================\n";
            UnityEngine.Debug.Log("pbxProjPath=>" + pbxProjPath);
            PBXProject pbxProj = new PBXProject();
            pbxProj.ReadFromFile(pbxProjPath);

            foreach (PBXParam param in pbxParams)
            {
                param.Edit(ref pbxProj);
            }

            pbxProj.WriteToFile(pbxProjPath);

            log += className + "\n";
            foreach (PBXParam param in pbxParams)
            {
                log += param.GetParamLog();
            }

            log += "\n======================= PBXProjectValue =======================";
            UnityEngine.Debug.Log(log);
        }

    }

    /// <summary>
    /// 設定項目のクラス。 
    /// </summary>
    public abstract class PBXParam
    {
        public enum ProjectType
        {
            Main = 1 << 0,
            Framework = 1 << 1,
        }
        /// <summary>
        /// 設定されているパラメータのログ。
        /// </summary>
        /// <returns>The parameter log.</returns>
        public abstract string GetParamLog();
        /// <summary>
        /// 設定の編集を行う
        /// </summary>
        /// <param name="pbxProj">Pbx projct.</param>
        public abstract void Edit(ref PBXProject pbxProj);

        /// <summary>
        /// プロジェクト名のリスト（基本的には、Unity-iPhoneのみ）
        /// </summary>
        private ProjectType m_projectType;


        /// <summary>
        /// プロジェクトのguidを取得する。
        /// </summary>
        /// <returns>The project guids.</returns>
        /// <param name="pbxProj">Pbx project.</param>
        protected IEnumerable<string> GetProjectGuids(PBXProject pbxProj)
        {
            var guids = new List<string>();
            if ((m_projectType & ProjectType.Main) == ProjectType.Main)
            {
                guids.Add(pbxProj.GetUnityMainTargetGuid());
            }
            if ((m_projectType & ProjectType.Framework) == ProjectType.Framework)
            {
                guids.Add(pbxProj.GetUnityFrameworkTargetGuid());
            }
            return guids;
        }

        /// <summary>
        /// コンストラクタ。継承先のクラスで必ず呼び出すこと（ GetProjectGuids()で使用します。 ）
        /// </summary>
        /// <param name="projectType">Project types.</param>
        public PBXParam(ProjectType projectType)
        {
            m_projectType = projectType;
        }
    }

    /// <summary>
    /// プロジェクトに設定されているファイルを追加するクラス。
    /// </summary>
    public class PBXParamFile : PBXParam
    {

        /// <summary> 編集を行うかどうか</summary>
        private readonly List<FileParam> m_params;

        /// <summary> Xcodeプロジェクトのディレクトリ</summary>
        private readonly string m_xcodePath;

        /// <summary>
        /// どのようなファイル操作を行うか。
        /// </summary>
        public enum FileEditType
        {
            /// <summary> ファイルの追加 </summary>
            ADD,
            /// <summary> ファイルのリネーム（移動）</summary>
            MOVE,
            /// <summary> ファイルの削除（物理削除は行わない）</summary>
            REMOVE,
        }

        /// <summary>
        /// 変更設定。
        /// </summary>
        public class FileParam
        {

            /// <summary> edit type.</summary>
            public FileEditType Type { get; private set; }
            /// <summary> form directory path.</summary>
            public string FromDirPath { get; private set; }
            /// <summary> from file name.</summary>
            public string FromFileName { get; private set; }
            /// <summary> to directory path.</summary>
            public string ToDirPath { get; private set; }
            /// <summary> to file name.</summary>
            public string ToFileName { get; private set; }
            /// <summary> to file name.</summary>
            public string CompileFlags { get; private set; }
            public bool CodeSignOnCopy { get; private set; }
            public bool RemoveHeadersOnCopy { get; private set; }


            /// <summary>
            /// <para>指定するタイプによって引数の使い分けを行う。</para>
            /// <para></para>
            /// <para>ADD</para>
            /// <para>(Unityから自動で転送されないファイルをXcodeに転送してプロジェクトに追加する)</para>
            /// <para>_fromDir : Unity上(Assetsを除く)</para>
            /// <para>fromFile : Unity上のファイル名</para>
            /// <para>___toDir : Xcode上のディレクトリ</para>
            /// <para>__toFile : Xcode上のファイル名</para>
            /// <para></para>
            /// <para>MOVE</para>
            /// <para>(Xcode上からファイルを移動(リネーム)する)</para>
            /// <para>_fromDir : Xcode上で移動前のディレクトリ</para>
            /// <para>fromFile : Xcode上で移動前のファイル名</para>
            /// <para>___toDir : Xcode上で移動後のディレクトリ</para>
            /// <para>__toFile : Xcode上で移動後のファイル名</para>
            /// <para></para>
            /// <para>REMOVE</para>
            /// <para>(Xcode上からファイルを削除する)</para>
            /// <para>_fromDir : Xcode上のディレクトリ</para>
            /// <para>fromFile : Xcode上のファイル名</para>
            /// <para>___toDir : -</para>
            /// <para>__toFile : -</para>
            /// </summary>
            /// <param name="type">Type.</param>
            /// <param name="fromDir">From dir.</param>
            /// <param name="fromFile">From file.</param>
            /// <param name="toDir">To dir.</param>
            /// <param name="toFile">To file.</param>
            /// <param name="compileFlags">Compile Flags.</param>
            public FileParam(FileEditType type, string fromDir, string fromFile, string toDir = null, string toFile = null, string compileFlags = null, bool codeSignOnCopy = false, bool removeHeadersOnCopy = false)
            {
                if (type != FileEditType.REMOVE)
                {
                    var param = "";
                    var isNull = false;
                    if (toDir == null)
                    {
                        param += "toDir";
                        isNull = true;
                    }
                    if (toFile == null)
                    {
                        if (isNull)
                        {
                            param += ", ";
                        }
                        param += "toFile";
                    }
                    if (isNull)
                    {
                        throw new System.ArgumentNullException(param, "If type is not REMOVE, you must set toXXX parameter!");
                    }
                }
                if (toFile == null)
                {
                    toFile = fromFile;
                }
                if (toDir == null)
                {
                    toDir = fromDir;
                }
                this.Type = type;
                this.FromDirPath = fromDir;
                this.FromFileName = fromFile;
                this.ToDirPath = toDir;
                this.ToFileName = toFile;
                this.CompileFlags = compileFlags;
                this.CodeSignOnCopy = codeSignOnCopy;
                this.RemoveHeadersOnCopy = removeHeadersOnCopy;
            }

            /// <summary>
            /// ログを作成.
            /// </summary>
            public string CreateLog()
            {
                var log = "[File:";

                switch (Type)
                {
                    case FileEditType.ADD:
                        log += "Add]" + "\n\tUnity file: " + CombinePath(FromDirPath, FromFileName) + "\n\tXcode file: " + CombinePath(ToDirPath, ToFileName + "\n\tCompile flags: " + CompileFlags);
                        break;
                    case FileEditType.MOVE:
                        log += "Move]" + "\n\tXC before file: " + CombinePath(FromDirPath, FromFileName) + "\n\tXC  after file: " + CombinePath(ToDirPath, ToFileName + "\n\tCompile flags: " + CompileFlags);
                        break;
                    case FileEditType.REMOVE:
                        log += "Delete]" + "\n\tDelete file: " + CombinePath(FromDirPath, FromFileName);
                        break;
                }
                return log;
            }
        }

        /// <summary>
        /// 設定されているパラメータのログ。
        /// </summary>
        /// <returns>The parameter log.</returns>
        public override string GetParamLog()
        {
            var log = "";

            foreach (FileParam param in m_params)
            {
                log += param.CreateLog() + "\n";
            }
            return log;
        }

        /// <summary>
        /// 設定の編集を行う
        /// </summary>
        /// <param name="pbxProj">Pbx projct.</param>
        public override void Edit(ref PBXProject pbxProj)
        {
            foreach (FileParam param in m_params)
            {
                switch (param.Type)
                {
                    case FileEditType.ADD:
                        {
                            // ファイルを追加する
                            AddFile(ref pbxProj, param);
                            break;
                        }
                    case FileEditType.MOVE:
                        {
                            // ファイルのリネームや移動
                            MoveFile(ref pbxProj, param);
                            break;
                        }
                    case FileEditType.REMOVE:
                        {
                            // ファイルの削除
                            RemoveFile(ref pbxProj, param);
                            break;
                        }
                }
            }
        }

        /// <summary>
        /// Unity上にはあるが、プロジェクトに追加されていないファイルをXcodeプロジェクトに追加する。
        /// </summary>
        /// <param name="pbxProj">Pbx proj.</param>
        /// <param name="param">Parameter.</param>
        private void AddFile(ref PBXProject pbxProj, FileParam param)
        {
            // UnityのファイルをXcodeにコピーする
            var unityPath = CombinePath(UnityEngine.Application.dataPath, param.FromDirPath, param.FromFileName);
            var xcodePath = CombinePath(m_xcodePath, param.ToDirPath, param.ToFileName);
            CopyFile(unityPath, xcodePath);
            var addPath = CombinePath(param.ToDirPath, param.ToFileName);
            AddFileProc(ref pbxProj, addPath, param);
        }

        /// <summary>
        /// Xcodeにファイルを追加する
        /// </summary>
        /// <param name="pbxProj">Pbx proj.</param>
        /// <param name="addPath">Add path.</param>
        /// <param name="compileFlag">Compile flag.</param>
        private void AddFileProc(ref PBXProject pbxProj, string addPath, FileParam param)// string compileFlag)
        {
            // ファイル参照をプロジェクトに追加
            var fileGuid = pbxProj.AddFile(addPath, addPath);

            // プロジェクトの設定(BuildPhase)に追加
            foreach (var guid in GetProjectGuids(pbxProj))
            {
                if (param.CodeSignOnCopy) PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProj, guid, fileGuid);
                pbxProj.AddFileToBuildWithFlags(guid, fileGuid, param.CompileFlags);
            }
        }

        /// <summary>
        /// Xcodeのファイルを移動（リネーム）する
        /// </summary>
        /// <param name="pbxProj">Pbx proj.</param>
        /// <param name="param">Parameter.</param>
        private void MoveFile(ref PBXProject pbxProj, FileParam param)
        {
            var beforePath = CombinePath(param.FromDirPath, param.FromFileName);
            var afterPath = CombinePath(param.ToDirPath, param.ToFileName);
            // 既存のファイルを削除
            RemoveFileProc(ref pbxProj, beforePath);

            // 既存のファイルのリネーム
            MoveFile(CombinePath(m_xcodePath, beforePath), CombinePath(m_xcodePath, afterPath));

            // リネーム後ファイルの追加
            AddFileProc(ref pbxProj, afterPath, param);
        }

        /// <summary>
        /// Xcodeに追加されているファイルの削除（物理削除は行わない）
        /// </summary>
        /// <param name="pbxProj">Pbx proj.</param>
        /// <param name="param">Parameter.</param>
        private void RemoveFile(ref PBXProject pbxProj, FileParam param)
        {
            var removePath = CombinePath(param.FromDirPath, param.FromFileName);
            RemoveFileProc(ref pbxProj, removePath);
        }

        /// <summary>
        /// Xcodeに追加されているファイルの削除（物理削除は行わない）
        /// </summary>
        /// <param name="pbxProj">Pbx proj.</param>
        /// <param name="removePath">Remove path.</param>
        private void RemoveFileProc(ref PBXProject pbxProj, string removePath)
        {
            if (!pbxProj.ContainsFileByProjectPath(removePath)) return;

            var fileGuid = pbxProj.FindFileGuidByProjectPath(removePath);
            foreach (var guid in GetProjectGuids(pbxProj))
            {
                pbxProj.RemoveFileFromBuild(guid, fileGuid);
            }
            pbxProj.RemoveFile(fileGuid);
        }

        /// <summary>
        /// ファイルの親ディレクトリを作成する。
        /// </summary>
        /// <param name="filePath">File path.</param>
        public static void CreateParentDirectory(string filePath)
        {
            var files = filePath.Split("/"[0]);

            string path = null;
            for (int i = 0; i < files.Length - 1; ++i)
            {
                path = path == null ? files[i] : path + "/" + files[i];
                if (string.IsNullOrEmpty(path)) continue;
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
        }
        /// <summary>
        /// ファイルをコピーする.
        /// </summary>
        /// <param name="fromFile">From file.</param>
        /// <param name="toFile">To file.</param>
        public static void CopyFile(string fromFile, string toFile)
        {
            CreateParentDirectory(toFile);
            FileUtil.CopyFileOrDirectory(fromFile, toFile);
        }

        /// <summary>
        /// ファイルを移動（リネーム）する。
        /// </summary>
        /// <param name="fromFile">From file.</param>
        /// <param name="toFile">To file.</param>
        public static void MoveFile(string fromFile, string toFile)
        {
            CreateParentDirectory(toFile);
            FileUtil.MoveFileOrDirectory(fromFile, toFile);
        }

        /// <summary>
        /// パスの結合
        /// </summary>
        /// <returns>The path.</returns>
        /// <param name="fileAndDir">File and dir.</param>
        public static string CombinePath(params string[] fileAndDir)
        {
            string root = fileAndDir[0];
            for (int i = 1; i < fileAndDir.Length; ++i)
            {
                root = Path.Combine(root, fileAndDir[i]);
            }
            return root;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="parameters">Parameters.</param>
        /// <param name="xcodePath">Xcode path.</param>
        /// <param name="projectNames">Project names.</param>
        public PBXParamFile(List<FileParam> parameters, string xcodePath, ProjectType projectType = ProjectType.Main) : base(projectType)
        {
            m_params = parameters;
            m_xcodePath = xcodePath;
        }
    }

    /// <summary>
    /// Framework変更
    /// </summary>
    public class PBXParamFramework : PBXParam
    {

        /// <summary>
        /// Framework設定
        /// </summary>
        private readonly Dictionary<string, int> m_framemworks;

        /// <summary>
        /// ログ用の文字列作成
        /// </summary>
        /// <returns>The parameter log.</returns>
        public override string GetParamLog()
        {
            string log = "";
            foreach (KeyValuePair<string, int> framework in m_framemworks)
            {
                log += "[Framework] " + framework.Key + ": " +
                (
                    (framework.Value == -1) ? "Delete!!" :
                    (
                        (framework.Value == 1) ? "Optional" : "Required"
                    )
                ) + "\n";
            }
            return log;
        }

        /// <summary>
        /// 設定の編集を行う
        /// </summary>
        /// <param name="pbxProj">Pbx projct.</param>
        public override void Edit(ref PBXProject pbxProj)
        {
            IEnumerable<string> guids = GetProjectGuids(pbxProj);
            foreach (string guid in guids)
            {
                foreach (KeyValuePair<string, int> framemwork in m_framemworks)
                {
                    if (framemwork.Value == -1)
                        pbxProj.RemoveFrameworkFromProject(guid, framemwork.Key);
                    else
                        pbxProj.AddFrameworkToProject(guid, framemwork.Key, framemwork.Value == 1);
                }
            }
        }

        /// <summary>
        /// コンストラクタ
        /// 
        /// new Dictionary<string, int>() {
        ///     { "value1", 0 }, // Required
        ///     { "value2", 1 }, // Optional
        ///     { "value3", -1 },// 削除
        /// } 
        /// </summary>
        /// <param name="framemworks">Framemworks.</param>
        /// <param name="projectNames">Project names.</param>
        public PBXParamFramework(Dictionary<string, int> framemworks, ProjectType projectType = ProjectType.Main) : base(projectType)
        {
            this.m_framemworks = framemworks;
        }
    }

    /// <summary>
    /// プロパティ変更
    /// </summary>
    public class PBXParamProperty : PBXParam
    {

        /// <summary>
        /// プロパティ設定
        /// </summary>
        private readonly Dictionary<string, List<PropertyParams>> m_params;

        /// <summary>
        /// プロパティ設定項目
        /// </summary>
        public class PropertyParams
        {

            /// <summary> 設定を行うタイプ（Debug/Releaseなど） </summary>
            public string ConfigType { get; private set; }
            /// <summary> 追加する値</summary>
            public string[] AddValues { get; private set; }
            /// <summary> 削除する値</summary>
            public string[] RemoveValues { get; private set; }
            /// <summary> 既存の項目をむしして上書きするかどうか。</summary>
            public bool ForceOverride { get; private set; }

            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="configType">設定を行うコンフィグのタイプ.nullの場合はすべての設定に反映されます.</param>
            /// <param name="addValue">追加する値の配列.</param>
            /// <param name="removeValue">削除する値の配列.</param>
            /// <param name="forceOverride">強制的に上書きを行うかどうか.</param>
            public PropertyParams(string[] addValue, string[] removeValue = null, string configType = null, bool forceOverride = false)
            {
                this.ConfigType = configType;
                this.AddValues = addValue;
                this.RemoveValues = removeValue;
                this.ForceOverride = forceOverride;
            }
            /// <summary>
            /// 単一の値を全オプションに反映する用の簡易コンストラクタ
            /// </summary>
            /// <param name="addValue">追加する値</param>
            /// <returns></returns>
            public PropertyParams(string addValue) : this(new string[] { addValue }) { }


            /// <summary>
            /// ログの作成
            /// </summary>
            /// <returns>The log.</returns>
            public string CraeteLog()
            {
                var add = "[";
                for (int i = 0; AddValues != null && i < AddValues.Length; ++i)
                {
                    add += AddValues[i];
                    if (AddValues.Length - 1 != i)
                    {
                        add += ",";
                    }
                }
                add += "]";

                var remove = "[";
                for (int i = 0; RemoveValues != null && i < RemoveValues.Length; ++i)
                {
                    remove += RemoveValues[i];
                    if (RemoveValues.Length - 1 != i)
                    {
                        remove += ",";
                    }
                }
                remove += "]";

                return "\n\tConfig Type: " + ConfigType + "\n\tadd values: " + add + "\n\tremove values: " + remove;
            }
        }

        /// <summary>
        /// コンフィグ設定のguidを取得
        /// </summary>
        /// <returns>The config guids.</returns>
        /// <param name="pbxProj">Pbx proj.</param>
        /// <param name="configName">Config name.</param>
        private IEnumerable<string> GetConfigGuids(PBXProject pbxProj, string configName)
        {
            IEnumerable<string> projects = GetProjectGuids(pbxProj);
            List<string> guids = new List<string>();
            foreach (string guid in projects)
            {
                guids.Add(pbxProj.BuildConfigByName(guid, configName));
            }
            return guids;
        }

        /// <summary>
        /// ログの作成
        /// </summary>
        /// <returns>The parameter log.</returns>
        public override string GetParamLog()
        {
            var log = "";
            foreach (KeyValuePair<string, List<PropertyParams>> kv in m_params)
            {
                foreach (PropertyParams param in kv.Value)
                {
                    log += "[Property] " + kv.Key + ":" + param.CraeteLog() + "\n";
                }
            }
            return log;
        }

        /// <summary>
        /// 設定の編集を行う
        /// </summary>
        /// <param name="pbxProj">Pbx projct.</param>
        public override void Edit(ref PBXProject pbxProj)
        {
            foreach (KeyValuePair<string, List<PropertyParams>> param in m_params)
            {
                var key = param.Key;
                foreach (PropertyParams propertyItem in param.Value)
                {
                    IEnumerable<string> guids = propertyItem.ConfigType == null ? GetProjectGuids(pbxProj) : GetConfigGuids(pbxProj, propertyItem.ConfigType);
                    // 既存設定を無視して上書き
                    if (propertyItem.ForceOverride)
                    {
                        int i = 0;
                        if (propertyItem.ConfigType == null)
                        {
                            foreach (string addValue in propertyItem.AddValues)
                            {
                                if (i == 0)
                                    pbxProj.SetBuildProperty(guids, key, addValue);
                                else
                                    pbxProj.AddBuildProperty(guids, key, addValue);
                            }
                            ++i;
                        }
                        else
                        {
                            guids = GetConfigGuids(pbxProj, propertyItem.ConfigType);
                            foreach (string addValue in propertyItem.AddValues)
                            {
                                if (i == 0)
                                    pbxProj.SetBuildPropertyForConfig(guids, key, addValue);
                                else
                                    pbxProj.AddBuildPropertyForConfig(guids, key, addValue);
                            }
                            ++i;
                        }
                    }
                    else
                    {
                        if (propertyItem.ConfigType == null)
                        {
                            pbxProj.UpdateBuildProperty(guids, key, propertyItem.AddValues, propertyItem.RemoveValues);
                        }
                        else
                        {
                            /* 
                                 * UpdateBuildPropertyForConfig( IEnumerator<string>, stirng, string[], string[] )
                                 * は内部的に、UpdateBuildProperty( string, string, string[], string[] )をコールしており、
                                 * UpdateBuildPropertyForConfig( string, string, string[], string[] )と挙動が異なるので、foreachでguidはループで指定する
                                 */
                            foreach (string guid in guids)
                            {
                                pbxProj.UpdateBuildPropertyForConfig(guid, key, propertyItem.AddValues, propertyItem.RemoveValues);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="parameters">Parameters.</param>
        /// <param name="projectNames">Project names.</param>
        public PBXParamProperty(Dictionary<string, List<PropertyParams>> parameters, ProjectType projectType = ProjectType.Main) : base(projectType)
        {
            this.m_params = parameters;
        }
        /// <summary>
        /// コンストラクタ string のみで簡易設定用
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="projectType"></param>
        /// <returns></returns>
        public PBXParamProperty(Dictionary<string, string> parameters, ProjectType projectType = ProjectType.Main) : base(projectType)
        {
            m_params = new Dictionary<string, List<PropertyParams>>();
            foreach (var param in parameters)
            {
                m_params[param.Key] = new List<PropertyParams> { new PropertyParams(param.Value) };
            }
        }
    }
}
#endif
```

## PlistEditor.cs
```csharp:PlistEditor.cs
#if UNITY_IOS
/**
 * @file     PlistEditor.cs
 * @brief    plistの編集を行うクラス。
 * @date     $Date$
 * @version  $Rev$
 * @author   $Author$
 */
using UnityEditor.iOS.Xcode;
using System;
using System.IO;
using System.Collections.Generic;

namespace yyama2.Editor.XCProjEdit {

	/// <summary>
	/// plistの値を変更する。
	/// 
	/// 
	/// 値の設定は、下記のように定義してください。
	/// ※キーは任意の文字列です。
	/// 
	/// int 系のデータ
	/// { "int", 1 },
	/// boolean系の値
	/// { "bool", true },
	/// 文字列
	/// { "string", "string value" },
	/// 配列
	/// { "list", new List<object>() { 1, 2, 3 } },
	/// Dict
	/// { "dict", new Dictionary<string, object>() {
	/// 		{ "value1", "string value" },
	/// 		{ "value2", 1 },
	/// 		{ "value3", true },
	/// 	} 
	/// },
	/// </summary>
	public class PlistEditor {

		/// <summary> Info.plistのパスを取得する</summary>
		/// <param name="path"> Info.plistのあるフォルダパス</param>
		/// <returns> Info.plistのファイルパス</returns>
		public static string GetInfoPlistPath( string path )
		{
			return Path.Combine( path, "Info.plist" );
		}

		/// <summary>
		/// plistを編集する。
		/// </summary>
		/// <param name="plistPath"> plistファイルパス</param>
		/// <param name="values">追加する値.</param>
		/// <param name="callClass">この関数を呼び出しているクラス.</param>
		public static void EditPlist( string plistPath, Dictionary<string, object> values, Type callClass )
		{
			string className = "";

			if( callClass != null )
			{
				className += callClass.Name;
			}
			UnityEngine.Debug.Log( "PlistEditor:" + className );
			string log = "======================= PlistValue =======================\n";
			UnityEngine.Debug.Log( "plistPath=>" + plistPath );
			PlistDocument plist = new PlistDocument();
			plist.ReadFromFile( plistPath );

			foreach( KeyValuePair<string, object> value in values )
			{
				setValue( value.Key, value.Value, ref plist.root );
			}

			// Documentsフォルダを共有可能にする
			plist.WriteToFile( plistPath );

			log += className + "\n";
			log += plistPath +"\n{";
			foreach( var value in values )
			{
				CreateLog( value.Key, value.Value, 0, ref log );
			}
			log += "\n}\n" + "======================= PlistValue =======================";
			UnityEngine.Debug.Log( log );
		}

		/// <summary>
		/// 値をセットする
		/// </summary>
		/// <param name="key"> キー.</param>
		/// <param name="value"> 値.</param>
		/// <param name="dict"> Dictオブジェクト.</param>
		private static void setValue( string key, object value, ref PlistElementDict dict )
		{
			bool validValue = true;
			Type type = value.GetType();

			// int .
			if( type == typeof(int) )
			{
				dict.SetInteger( key, (int)value );
			}
			// bool .
			else if( type == typeof(bool) )
			{
				dict.SetBoolean( key, (bool)value );
			}
			// string .
			else if( type == typeof(string) )
			{
				dict.SetString( key, (string)value );
			}
			// list
			else if( type == typeof(List<object>) )
			{
				PlistElementArray subArray;

				if( !tryGetValue<PlistElementArray>( key, dict, out subArray ) )
				{
					subArray = dict.CreateArray( key );
				}

				List<object> list = (List<object>)value;
				foreach( object val in list )
				{
					addValue( val, ref subArray );
				}
			}
			// dictionary
			else if( type == typeof(Dictionary<string, object>) )
			{
				PlistElementDict subDict;

				if( !tryGetValue<PlistElementDict>( key, dict, out subDict ) )
				{
					subDict = dict.CreateDict( key );
				}

				Dictionary<string, object> dictionary = (Dictionary<string, object>)value;
				foreach( var dictItem in dictionary )
				{
					setValue( dictItem.Key, dictItem.Value, ref subDict );
				}
			}
			else validValue = false;

			// invalid parameter
			if( !validValue )
			{
				UnityEngine.Debug.LogError( "Plist Editor.  Invalid Parameter => " + key );
			}
		}

		/// <summary>
		/// リストに値をセットする
		/// </summary>
		/// <param name="value">値.</param>
		/// <param name="array">Arrayオブジェクト.</param>
		private static void addValue( object value, ref PlistElementArray array )
		{
			Type type = value.GetType();

			// int .
			if( type == typeof(int) )
			{
				array.AddInteger( (int)value );
			}
			// bool .
			else if( type == typeof(bool) )
			{
				array.AddBoolean( (bool)value );
			}
			// string .
			else if( type == typeof(string) )
			{
				array.AddString( (string)value );
			}
			// list
			else if( type == typeof(List<object>) )
			{
				PlistElementArray subArray = array.AddArray();
				List<object> list = (List<object>)value;
				foreach( object val in list )
				{
					addValue( val, ref subArray );
				}
			}
			// dictionary
			else if( type == typeof(Dictionary<string, object>) )
			{
				PlistElementDict subDict = array.AddDict();
				Dictionary<string, object> dictionary = (Dictionary<string, object>)value;
				foreach( var dictItem in dictionary )
				{
					setValue( dictItem.Key, dictItem.Value, ref subDict );
				}
			}
			else
			{
				UnityEngine.Debug.LogError( "Add List Error!!" );
			}
		}

		/// <summary>
		/// dictオブジェクトから値を抽出する
		/// </summary>
		/// <returns><c>true</c>, if success to get, <c>false</c> otherwise.</returns>
		/// <param name="key">Key.</param>
		/// <param name="dict">Dictオブジェクト.</param>
		/// <param name="val">Value.</param>
		/// <typeparam name="T">The 1st type parameter.</typeparam>
		private static bool tryGetValue<T>( string key, PlistElementDict dict, out T val ) where T : PlistElement
		{
			IDictionary<string, PlistElement> dictValue = dict.values;
			PlistElement el;
			val = null;
			if( dictValue.TryGetValue( key, out el ) )
			{
				val = el as T;
			}
			return val != null;
		}

		/// <summary>
		/// ログ様の文字列作成 .
		/// </summary>
		/// <param name="key">Key.</param>
		/// <param name="value">Value.</param>
		/// <param name="indent">Indent.</param>
		/// <param name="log">Log.</param>
		private static void CreateLog( string key, object value, int indent, ref string log )
		{
			++indent;
			string indentTabs = "";
			for( int i = 0; i < indent; ++i )
			{
				indentTabs += "\t";
			}
			log += "\n" + indentTabs + key + ":";
			Type type = value.GetType();
			// int .
			if( type == typeof(int) )
			{
				log += value;
			}
			// bool .
			else if( type == typeof(bool) )
			{
				log += value;
			}
			// string .
			else if( type == typeof(string) )
			{
				log += "\"" + value + "\"";
			}
			// list
			else if( type == typeof(List<object>) )
			{
				log += "[";
				List<object> list = (List<object>)value;
				for( int i = 0; i < list.Count; ++i )
				{
					CreateLog( "["+ i +"]", list[i], indent, ref log);
				}
				log += "\n" + indentTabs + "]";
			}
			// dictionary
			else if( type == typeof(Dictionary<string, object>) )
			{
				Dictionary<string, object> dictionary = (Dictionary<string, object>)value;
				log += "{";			
				foreach( var dictItem in dictionary )
				{
					CreateLog( dictItem.Key, dictItem.Value, indent, ref log );
				}

				log += "\n" + indentTabs + "}";
			}
			log += ",";
		}
	}
}
#endif
```