Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Unity に於ける iOS ビルド自動化のおはなし【実践編】
Search
Tetsuya Mori
PRO
October 30, 2014
Programming
280
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Unity に於ける iOS ビルド自動化のおはなし【実践編】
Tetsuya Mori
PRO
October 30, 2014
More Decks by Tetsuya Mori
See All by Tetsuya Mori
ビルドシステムを C# 大統一したお話
monry
PRO
2
1.5k
C# 大統一理論推進委員会 会員のための Unity Package Manager プロジェクト構成案
monry
PRO
0
1.8k
はじめての C# 大統一理論
monry
PRO
0
850
unity1week でチーム開発を成功させる秘訣
monry
PRO
0
330
Unity で ARゲームを作った話
monry
PRO
1
1k
Visual Programming Framework UniFlow のご紹介/Introducing UniFlow
monry
PRO
2
3.2k
UniFlow 完全に理解した / I can develop UniFlow a little.
monry
PRO
0
560
Democratized Unity Package Manager
monry
PRO
1
3.1k
使ってみよう! Unity RemoteSettings/Let's use Unity RemoteSettings
monry
PRO
1
1.3k
Other Decks in Programming
See All in Programming
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
380
VibeCodingからAgenticWorkflowへ
starfish719
0
490
AIを紡ぐPMのお話
swdtkuy
0
110
What's New in Android 2026
veronikapj
0
260
今さら聞けない .NET CLI
htkym
0
190
為什麼你並不需要ViewModel / No, you don't need a ViewModel
lovee
1
500
Go 1.27 における memory allocation の高速化
andpad
0
190
ソフトウェアエンジニアにとっての生成AI - 特性を知って使い倒す / generative ai for software enginner
kishida
5
1.4k
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
12
18k
Flow は今どうなっているか
mizdra
PRO
0
560
使いながら育てる Claude Code — 開発フローの1コマンド化 × 繰り返し指摘の自動仕組み化
shiki_kakaku
0
1.8k
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
2
280
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
GitHub's CSS Performance
jonrohan
1033
470k
Six Lessons from altMBA
skipperchong
29
4.4k
The Cost Of JavaScript in 2023
addyosmani
55
10k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.8k
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
Done Done
chrislema
186
16k
Become a Pro
speakerdeck
PRO
31
6.2k
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Technical Leadership for Architectural Decision Making
baasie
3
480
Facilitating Awesome Meetings
lara
57
7.1k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
570
Transcript
6OJUZʹԙ͚Δ J04ϏϧυࣗಈԽͷ ͓ͳ͠ʲ࣮ફฤʳ 5IV 6OJUZษڧձWPM גࣜձࣾΩοζελʔ γεςϜσϕϩϓϝϯτνʔϜϦʔμʔ ɹ࠸
͜ΜΜʂ
ʮ·͓ͨલ͔ʯ ͱ͔ݴΘͳ͍ɻ ΜΓ͌Ͱͬ͢ʂ
ࠓͷςʔϚ
None
ͱɺ͍͏Θ͚Ͱ Ϗϧυ ͷ͓ͳ͠Ͱ͢
͓͠ͳ͕͖
͓͠ͳ͕͖ w ࣗݾհ w લஔ͖ w Δ͜ͱ w Γ͔ͨ
ࣗݾհ
XIPBNJ w lɹ࠸zͱਃ͠·͢ɻ w BLBΜΓ͌5!NPOSZ'NPOSZ w ࡀˁ0ܕఱṝ࠲طࠗ w झຯʮ͓ञʯͱʮ߹এʯͰ͢ɻ
XIPBNJ w େֶΛ ࣗओతʹ ଔۀޙɺϕϯνϟʔΛసʑɻ w 8FCͷϑϩϯτΤϯυɺαʔόαΠυ͕ಘҙɻ w 6OJUZྺ͘Β͍ɻ w
"84ͱΩϟοΩϟϑϑ͢Δͷ͖Ͱ͢ɻ w ઈࢍ4IBEFSͷ͓ษڧதɻ
KPCT w lגࣜձࣾΩοζελʔzͬͯձࣾͰಇ͍ͯ·͢ɻ w ະबֶࣇʙখֶੜͷ͓ࢠ͞·Λ͓࣋ͪͷ ϑΝϛϦʔΛλʔήοτʹͨ͠ɺҭڭҭʹ ؔΘΔΞϓϦɾαʔϏεΛల։͓ͯ͠Γ·͢ɻ w ͓ӄ༷Ͱ&E5FDIͳҭʹԙ͍ͯ /PنͰల։͓ͯ͠Γ·͢ʂ
ࣾௐ͆
MTMBBQQT ͳΓ͖Γ ͬ͜͝ϥϯυ ύζϧςΠϧζ ͓͔͠ͷ͘ʹΛ ͭ͘Δͷ͡Ό ͳΓ͖Γ ΞΠεΫϦʔϜ ͞Μͬ͜͝ ͳΓ͖Γ
ϚϚͬ͜͝ ͓หΛͭ͘Ζ͏ʂ ͓͔͠ͷՈΛ ͭ͘Ζ͏ʂ ϋϯόʔΨʔ ͞Μͬ͜͝ ඈߦػΛ ΈཱͯΑ͏ʂ i18n i18n i18n i18n Ώ͔͍ͳ ͓Ֆ͞Μ App Store / Google Play カテゴリランキング1位 多数獲得!!!
લஔ͖
ڥ w 0QFSBUJPO4ZTUFN049:PTFNJUF w 6OJUZG 1SP0OMZ w 9DPEF XJUI$PNNJOE-JOF5PPMT
w 1MBUGPSNJ04 w -BOHVBHF$ w 0UIFS9DPEF&EJUPSGPS6OJUZ GPSLFECZNPOSZ
ࠓΔίτ w l#VJMEzϘλϯΛԡͨ͠Ҏ߱ͷ w ۃྗ9DPEFΛૢ࡞͠ͳ͍ͬͯ w 1PTU1SPDFTT#VJMEͳ
ࠓΒͳ͍͜ͱ w /BUJWF1MVHJOͷ w J5VOFT$POOFDUͰͷਃͷ
Δ͜ͱ
6OJUZ㱺9DPEF
6OJUZ㱺9DPEF ͜ͷϘλϯΛԡ͢ͱʜ
6OJUZ㱺9DPEF ίϨΛܗʹͯ͠ʜ
6OJUZ㱺9DPEF ͜Μͳͷ͕ग़ྗ͞Εʜ
6OJUZ㱺9DPEF ͍ͭ͜ʹϓϩδΣΫτใͱ͔͕ఆٛ͞ΕΔ
"SDIJWF
1SF"SDIJWF w ΞΠίϯઃఆ w "3$ͳ/BUJWF1MVHJOGPCKDBSDͷઃఆ w 0UIFS-JOLFS'MBHTͷઃఆ w Ճ'SBNFXPSLͷઃఆ w
ΞΠίϯԼͷΞϓϦ໊ઃఆ w 63-4DIFNFTઃఆ
1BDLBHJOH ίϨΛग़ྗ͢Δ
1BDLBHJOH 9DPEF1SPKFDUͱ1SPWJTJPOJOH1SPpMF͕ॏཁ º
1BDLBHJOH 9DPEFͷϝχϡʔ͔Β<1SPEVDU><"SDIJWF>
1BDLBHJOH 0SHBOJ[FSͰ<&YQPSU>Ϙλϯԡͯ͠ʜ
1BDLBHJOH ςελʔ͞ΜʹΔ༻ͷJQB࡞ͬͨΓͱ͔Ͷɻ
͜͜·Ͱͷ࡞ۀΛ ৬ਓ͕୮ਫ਼ࠐΊͯ ख࡞ۀͰɻ
@ dᵲᵲʏźŕũŽƀŕŜ
͡Ό͋ʜ
͍ͭࣗಈԽ͢Δͷʂʁ
ࠓͰ͠ΐʂ
Γ͔ͨ
&YQPSU9DPEF1SPKFDU
&YQPSU9DPEF1SPKFDU UnityEditor.BuildPipeline.BuildPlayer( string[] levels, string locationPathName, UnityEditor.BuildTarget target, UnityEditor.BuildOptions options
); ઌͣجຊ͔Β
&YQPSU9DPEF1SPKFDU BuildPipeline.BuildPlayer( new string[] { "Main.scene" }, "/Users/monry/SampleProject", BuildTarget.iPhone, BuildOptions.Development
| BuildOptions.AllowDebugging ); &YBNQMF ͜ΕͰ9DPEF1SPKFDU͕ग़ྗ͞ΕΔ
ίί͔Βઌ 1PTU1SPDFTT#VJME Ͱॲཧ͢Δ
"SDIJWF
"SDIJWF 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(); ઌͣYDPEFCVJMEίϚϯυ࣮ߦ 1SPWJTJPOJOHͷ*%J1IPOFߏϢʔςΟϦςΟ ͱ͔ͰௐΔײ͡Ͱɻ
"SDIJWF 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(); ଓ͍ͯYDSVOίϚϯυ࣮ߦ ϓϩδΣΫτ໊ɺ1MBZFS4FUUJOHTͷ 1SPEVDU/BNFΛࢦఆ͢Δ
ΞΠίϯઃఆ
ΞΠίϯઃఆ 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 ); J1IPOF1MVT༻ͷΞΠίϯ͕ൈ͚ΔͷͰɻ ࠷৽ ͰରԠࡁ͔ʁ ௐͯͳ͍ɻ
GPCKDBSDઃఆ
GPCKDBSDઃఆ 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"); } } 9DPEF&EJUPSGPS6OJUZΛ͏ 6OJUZ/PO"3$ͳͷͰɺ"3$ͳ/BUJWF 1MVHJOʹϑϥάΛ͚ͯ͋͛Δඞཁ͕͋Δɻ
0UIFS-JOLFS'MBHTઃఆ
0UIFS-JOLFS'MBHTઃఆ XCProject project = new UnityEditor.XCodeEditor.XCProject("/path/to/export"); string[] flagNameList = new
string[] { "-ObjC", "- all_load", ... }; foreach (string flagName in flagNameList) { project.AddOtherLDFlags(flagName); } ಉ͘͡9DPEF&EJUPSGPS6OJUZ͏ ֎෦4%,ͱ͔͏ͱ͖ʹඞཁͩͬͨΓ͢Δɻ
Ճ'SBNFXPSLͷઃఆ
Ճ'SBNFXPSLͷઃఆ XCProject project = new UnityEditor.XCodeEditor.XCProject("/path/to/export"); project.ApplyMod(Path.Combine(Application.dataPath, "path/to/projmods")); ͬͺΓ9DPEF&EJUPSGPS6OJUZ͏ QSPKNPETͳΔϑΝΠϧΛ৯ΘͤΔ
Ճ'SBNFXPSLͷઃఆ { "group": "", "patches": [], "libs": [], "librarysearchpaths": [],
"frameworksearchpaths": [], "frameworks": [ "Security.framework", "CoreData.framework", ], "headerpaths": [], "files": [], "folders": [], "excludes": ["^.*\\.meta$", "^.*\\.mdown^", "^.*\\.pdf$"] } IPHFQSPKNPETͷαϯϓϧ +40/ܗࣜͰهड़ɻ ঢ়گʹԠͯ͡ଞͷΩʔઃఆͨ͠Γͱ͔ɻ
ΞΠίϯԼͷΞϓϦ໊
ΞΠίϯԼͷΞϓϦ໊ // Unity-iPhone/en.lproj/InfoPlist.strings CFBundleDisplayName = "HogeFuga"; // Unity-iPhone/ja.lproj/InfoPlist.strings CFBundleDisplayName =
"ほげふが"; ઌͣ*OGP1MJTUTUSJOHTΛ࡞Δ ϑΝΠϧ໊ͱ͔ίϨ͡Όແͯ͘0,
ΞΠίϯԼͷΞϓϦ໊ 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) ); } *OGP1MJTUTUSJOHTΛ7BSJBOU(SPVQʹՃ 7BSJBOU(SPVQରԠ൛͕ίνϥʹ͍͟͝·͢ɻ ,OPXO3FHJPOΛՃ͠ͳ͍ͱμϝɻ
63-4DIFNFTઃఆ
63-4DIFNFTઃఆ 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); JOGPQMJTU 9.- ΛΰϦΰϦॻ͖͑Δ จࣈͷτίɺॻ͖͑ͯͶɻ
͓·͚
͓·͚ w Ϗϧυͨ͠JQBΛ%FQMPZ(BUFʹࣗಈ164) ͢ΔͱɺΦγϟϨʂ w ߋʹͦͷ݁ՌΛ$IBU8PSLͳΓ4MBDLͳΓʹ ࣗಈ1045͢ΔͱɺͬͱΦγϟϨʂʂ w ߋʹߋʹɺίί·ͰΛ+FOLJOTΦδαϯͱ͔ʹ ͓ئ͍͢Δͱɺ࠷ߴʹΦγϟϨʂʂʂ
·ͱΊ
·ͱΊ w 1PTU1SPDFTT#VJMEͰɺجຊతͳઃఆΛஔ w #VJMEˠ"SDIJWFˠ%FQMPZࣗಈԽ͠Α͏ʂ
5IBOLZPVGPS ZPVSBUUFOUJPO