Unity に於ける iOS ビルド自動化の おはなし【実践編】
2014.10.30 (Thu) / Unity 勉強会 vol.26
株式会社キッズスター システムデベロプメントチーム リーダー
森 哲哉
こんばんは!
「またお前か」 とか言わない。もんりぃですっ!
今日のテーマ
と、いうわけで
ビルド のおはなしです
おしながき
おしながき
• 自己紹介
• 前置き
• やること
• やりかた
自己紹介
$ whoami
• “森 哲哉” と申します。
• a.k.a: もんりぃ / T: @monry / F: monry84
• 30歳 / ♂ / O型 / 天秤座 / 既婚
• 趣味は「お酒」と「合唱」です。
$ whoami• 大学を (自主的に) 卒業後、ベンチャーを転々。
• Web のフロントエンド、サーバサイドが得意。
• Unity 歴 1 年半くらい。
• AWS とキャッキャウフフするのも好きです。
• 絶賛 Shader のお勉強中。
$ jobs• “株式会社キッズスター” って会社で働いてます。
• 未就学児~小学生のお子さまをお持ちのファミリーをターゲットにした、知育/教育に関わるアプリ・サービスを展開しております。
• お陰様で EdTech な知育分野に於いてNo.1 規模で展開しております!(当社調べw)
$ ls -la apps/
なりきり!! ごっこランド
パズル&テイルズおかしのくにを つくるのじゃ!!
なりきり!! アイスクリーム 屋さんごっこ
なりきり!! ママごっこ
お弁当をつくろう!
おかしの家を つくろう!
ハンバーガー やさんごっこ
飛行機を 組み立てよう!
i18n i18n i18n
i18n
ゆかいな お花屋さん
App Sto
re / Goo
gle Play
カテゴリランキング1位
多数獲得!!!
前置き
環境• Operation System: OS X Yosemite
• Unity: 4.5.4f1 (Pro Only)
• Xcode: 6 (with Commind Line Tools)
• Platform: iOS
• Language: C#
• Other: Xcode Editor for Unity (forked by monry)
今日 喋るコト
• “Build” ボタンを押した以降の話
• 極力 Xcode を操作しないって話
• PostProcessBuild な話
今日 喋らないこと
• Native Plugin の話
• iTunes Connect での申請の話
やること
Unity ⇒ Xcode
Unity ⇒ Xcode
このボタンを押すと…
Unity ⇒ Xcode
コレを雛形にして…
Unity ⇒ Xcode
こんなのが出力され…
Unity ⇒ Xcode
こいつにプロジェクト情報とかが定義される
Archive
Pre Archive• アイコン設定
• ARC な Native Plugin へ -fobjc-arc の設定
• Other Linker Flags の設定
• 追加 Framework の設定
• アイコン下のアプリ名設定
• URLSchemes 設定
Packaging
コレを出力する
Packaging
Xcode Project と Provisioning Profile が重要
×
Packaging
Xcode のメニューから [ Product ] > [ Archive ]
Packaging
Organizer で [ Export ] ボタン押して…
Packaging
テスターさんに配る用の ipa 作ったりとかね。
ここまでの作業を 職人が丹精込めて 手作業で。
(/#-_-)/~┻┻〃ヤッテラレッカ
じゃあ…
いつ自動化するの!?
今でしょ!
やりかた
Export Xcode Project
Export Xcode Project
UnityEditor.BuildPipeline.BuildPlayer( string[] levels, string locationPathName, UnityEditor.BuildTarget target, UnityEditor.BuildOptions options );
先ずは基本から
Export Xcode Project
BuildPipeline.BuildPlayer( new string[] { "Main.scene" }, "/Users/monry/SampleProject", BuildTarget.iPhone, BuildOptions.Development | BuildOptions.AllowDebugging );
Example
これで Xcode Project が出力される
ココから先は PostProcessBuild で処理する
Archive
Archive
System.Diagnostics.Process process = new System.Diagnostics.Process(); process.StartInfo.FileName = "/usr/bin/xcodebuild"; process.StartInfo.Arguments = string.Format( "-project \"{0}/Unity-iPhone.xcodeproj\" -sdk iphoneos -target \"Unity-iPhone\" -configuration Release clean build CODE_SIGN_IDENTITY=\"iPhone Distribution\" PROVISIONING_PROFILE=\"{1}\"", "/path/to/export", "Provisioning Profile ID" ); process.StartInfo.CreateNoWindow = true; process.Start(); process.WaitForExit(); process.Close();
先ずは xcodebuild コマンド実行
Provisioning の ID は iPhone 構成ユーティリティとかで調べる感じで。
Archive
System.Diagnostics.Process process = new System.Diagnostics.Process(); process.StartInfo.FileName = "/usr/bin/xcrun"; process.StartInfo.Arguments = string.Format( "-sdk iphoneos PackageApplication \"{0}/build/{1}.app\" -o \"{0}/build/Unity-iPhone.ipa\" --embed \"{2}.mobileprovision\"", "/path/to/export", "SampleProject", // プロジェクト名 "Provisioning Profile ID" ); process.StartInfo.CreateNoWindow = true; process.Start(); process.WaitForExit(); process.Close();
続いて xcrun コマンド実行
プロジェクト名は、Player Settings の Product Name を指定する
アイコン設定
アイコン設定
string path = "/path/to/export"; File.Copy( Path.Combine(Application.dataPath, "/Path/to/Icon-180.png"), Path.Combine(path, "Unity-iPhone/Images.xcassets/AppIcon.appiconset/Icon-180.png"), true ); string jsonText = File.ReadAllText( Path.Combine(path, "Unity-iPhone/Images.xcassets/AppIcon.appiconset/Contents.json") ); jsonText = Regex.Replace( jsonText, "\t\\}\n\t\\],", "\t},\n\t\t\t{\n\t\t\"size\" : \"60x60\",\n\t\t\"idiom\" : \"iphone\",\n\t\t\"filename\" : \"Icon-180.png\",\n\t\t\"scale\" : \"3x\"\n\t}\n\t]," ); File.WriteAllText( Path.Combine( path, "Unity-iPhone/Images.xcassets/AppIcon.appiconset/Contents.json" ), jsonText );
iPhone 6/6 Plus 用のアイコンが抜けるので。
最新 (4.5.5) では対応済かも? (調べてない。)
-fobjc-arc 設定
-fobjc-arc 設定
XCProject project = new UnityEditor.XCodeEditor.XCProject("/path/to/export"); string[] filePathList = new string[] { "/path/to/file1", "/path/to/file2", ... }; foreach (string filePath in filePathList) { PBXBuildFile buildFile = project.GetBuildFile(project.GetFile(filePath)); if (null != buildFile) { buildFile.AddCompilerFlag("-fobjc-arc"); } }
Xcode Editor for Unity を使う
Unity は Non-ARC なので、ARC な Native Plugin にはフラグを付けてあげる必要がある。
Other Linker Flags 設定
Other Linker Flags 設定
XCProject project = new UnityEditor.XCodeEditor.XCProject("/path/to/export"); string[] flagNameList = new string[] { "-ObjC", "-all_load", ... }; foreach (string flagName in flagNameList) { project.AddOtherLDFlags(flagName); }
同じく Xcode Editor for Unity 使う
外部 SDK とか使うときに必要だったりする。
追加 Framework の設定
追加 Framework の設定
XCProject project = new UnityEditor.XCodeEditor.XCProject("/path/to/export"); project.ApplyMod(Path.Combine(Application.dataPath, "path/to/projmods"));
やっぱり Xcode Editor for Unity 使う
.projmods なるファイルを食わせる
追加 Framework の設定
{ "group": "", "patches": [], "libs": [], "librarysearchpaths": [], "frameworksearchpaths": [], "frameworks": [ "Security.framework", "CoreData.framework", ], "headerpaths": [], "files": [], "folders": [], "excludes": ["^.*\\.meta$", "^.*\\.mdown^", "^.*\\.pdf$"] }
hoge.projmods のサンプル
JSON 形式で記述。 状況に応じて他のキーも設定したりとか。
アイコン下のアプリ名
アイコン下のアプリ名
// Unity-iPhone/en.lproj/InfoPlist.strings CFBundleDisplayName = "HogeFuga";
// Unity-iPhone/ja.lproj/InfoPlist.strings CFBundleDisplayName = "ほげふが";
先ずは InfoPlist.strings を作る
ファイル名とかはコレじゃ無くても OK
アイコン下のアプリ名
XCProject project = new UnityEditor.XCodeEditor.XCProject("/path/to/export"); PBXVariantGroup infoPlist = project.GetVariantGroup("InfoPlist.strings", null, project.GetGroup("Supporting Files")); string[] languageCodeList = new string[] { "ja", "en" }; foreach (string languageCode in languageCodeList) { project.project.AddKnownRegion(languageCode); project.AddFile( Path.Combine(this.path, string.Format("Unity-iPhone/{0}.lproj/InfoPlist.strings", languageCode)), infoPlist, "SOURCE_ROOT", true, false, string.Format("{0}.lproj/InfoPlist.strings", languageCode) ); }
InfoPlist.strings を VariantGroup に追加
VariantGroup 対応版がコチラにございます。 KnownRegion を追加しないとダメ。
URLSchemes 設定
URLSchemes 設定
XmlDocument infoPlist = new XmlDocument(); infoPlist.Load(Path.Combine("/path/to/export", "Info.plist")); XmlNode infoPlistBaseNode = this.infoPlist.SelectSingleNode("/plist/dict");
// 一つ分の URL Type を表現するノードを構築 XmlElement _dict = infoPlist.CreateElement("dict"); _dict.AppendChild(infoPlist.CreateSimpleTextNode("key", "CFBundleTypeRole")); _dict.AppendChild(infoPlist.CreateSimpleTextNode("string", "Editor")); _dict.AppendChild(infoPlist.CreateSimpleTextNode("key", "CFBundleURLName")); _dict.AppendChild(infoPlist.CreateSimpleTextNode("string", "tv.kidsstar.app.${PRODUCT_NAME:rfc1034identifier}")); _dict.AppendChild(infoPlist.CreateSimpleTextNode("key", "CFBundleURLSchemes")); XmlElement _array = infoPlist.CreateElement("array"); _array.AppendChild(infoPlist.CreateSimpleTextNode("string", "tv.kidsstar.app.${PRODUCT_NAME:rfc1034identifier}")); _dict.AppendChild(_array);
// 作ったノードを key: CFBundleURLTypes の要素として追加 XmlNode _CFBundleURLTypesNode = infoPlist.SelectSingleNode("/plist/dict/array[preceding-sibling::key[.=\"CFBundleURLTypes\"]][1]"); if (null == _CFBundleURLTypesNode) { infoPlistBaseNode.AppendChild(infoPlist.CreateSimpleTextNode("key", "CFBundleURLTypes")); _CFBundleURLTypesNode = infoPlist.CreateElement("array"); infoPlistBaseNode.AppendChild(_CFBundleURLTypesNode); } _CFBundleURLTypesNode.AppendChild(_dict);
info.plist (XML) をゴリゴリ書き換える
赤文字のトコは、書き換えてね。
おまけ
おまけ• ビルドした ipa を DeployGate に自動 PUSH すると、オシャレ!
• 更にその結果を ChatWork なり Slack なりに 自動 POST すると、もっとオシャレ!!
• 更に更に、ココまでを Jenkins オジサンとかにお願いすると、最高にオシャレ!!!
まとめ
まとめ
• PostProcessBuild で、基本的な設定を置換
• Build → Archive → Deploy も自動化しよう!
Thank you foryour attention !