Unity Cloud BuildでiOSビルド時にCocoaPodsを利用する(ワイルドカード証明書)
Unity Cloud BuildでiOSビルド時にCocoaPodsを利用する(ワイルドカード証明書利用)
概要
この記事では、Unity Cloud BuildでiOSのビルドにCocoaPodsを利用する方法をまとめます。またビルドにはワイルドカード証明書を利用します。
本記事中で利用しているファイルは以下よりダウンロードできます。こちらを参考にして下さい。
XcodeFiles.zip
BuildPostProcess.zip
経緯
普段はWindowsで開発→Gitでソース共有→MacbookProでiOS用のビルドをしています。
Macbook Proが2012(late)と古くなってきたこともあり、いままで試していなかったUnityCloudBuildにチャレンジしました。
Google先生で調べたところ、以前はUnityCloudBuildでCocoaPodsが使えなかったようです。さすがにCocoaPodsが使えないと面倒です。
海外のフォーラム等ではCocoaPodsが使えるようになったよ!と記載があるのですが、UnityCloudBuild上で実行する方法がなかなか見つからず苦労しました。
なにかの参考になればと思いまとめています。
参考
CocoaPodsをUnity Cloud Buildで利用するに当たり、以下のBlog記事を大いに参考にさせて頂きました。
Automating Unity iOS builds
上記は、実際にUnity Cloud BuildでCocoaPodsを利用するに当たりありがたかったです。ファイルもZipでダウンロード可能です。
Unity Cloud BuildでiOSビルドに必要なMobile Provision Fileとp12 Fileを用意する
naichi’s labさんのblogでは、Unity Cloud Buildの設定で参考にさせて頂きました。
Unity Cloud Buildの設定
CocoaPodを使用するにあたり、UnityCloudBuildの設定は直接影響しません。
ビルドエラーが起きたりした場合の参考にして下さい。
- 使用するUnityバージョン:Latest 2018.x
- Xcodeバージョン:Xcode10.3
Unityバージョンは2019だとまだ不安があるので今回は2018です。
Xcodeはバージョン11だとIAPのCapabilityがどうのとエラーがでました。ワイルドカード証明書を利用しているため回避方法がわかりません。
Store提出用の証明書を利用している場合は、適切に権限を付与することでエラーは発生しないかと思います。
今回はワイルドカード証明書でとりあえずビルド確認を行いたいのでXcode10.3としています。
AdvancedOptionsですが、CompressionをLZ4HCにしています。ビルドサイズがが小さくなりますがその分時間はかかるかと思います。
テストではCompressionを設定しなくても良いかと思います。
また、はじめは「Custom Fastlane configuration Path」でFastlaneの設定を指定していました。FastlaneからCocoaPodsを利用しようと企んでいたためです。
実際にはうまくできずにやめました。
Unity Cloud Build側の設定は以上です。
PodFileの準備
PodFileとPodFileを実行するためのスクリプトファイルを準備します。
ファイルはこちらからダウンロードできます。
XcodeFiles.zip
今回は、プロジェクト直下(Assetsと同じ階層)に、XcodeFiles/Podディレクトリを作成します。
ポイントは、XcodeFilesディレクトリをプロジェクトのルート(Assets)と同じ階層に配置することです。
pods.commandの内容
1 2 3 4 |
#!/bin/bash cd "`dirname "$0"`" pod install open "./Unity-iPhone.xcworkspace" |
open_pods.commandの内容
1 2 3 |
#!/bin/bash cd "`dirname "$0"`" open pods.command |
PodFileの内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# Uncomment this line to define a global platform for your project source 'https://github.com/CocoaPods/Specs.git' source 'https://github.com/AppLovin/Public-Cocoapods-Specs.git' platform :ios, '9.0' target 'Unity-iPhone' do inherit! :search_paths pod 'Google-Mobile-Ads-SDK', '7.36' #pod 'GoogleMobileAdsMediationMaio' #pod 'GoogleMobileAdsMediationUnity' pod 'AppLovinSDK', ' ~>6.11' pod 'AppLovinMediationMintegralAdapter' pod 'AppLovinMediationAmazonAdapter' pod 'AppLovinMediationByteDanceAdapter' pod 'AppLovinMediationFacebookAdapter' pod 'AppLovinMediationGoogleAdapter' pod 'AppLovinMediationTapjoyAdapter' pod 'AppLovinMediationUnityAdsAdapter' pod 'AppLovinMediationVungleAdapter' pod 'Firebase/Core' pod 'Firebase/Messaging' pod 'Firebase/Database' pod 'Firebase/Storage' pod 'Firebase/Auth' pod 'TenjinSDK' # Uncomment this line if you're using Swift or would like to use dynamic frameworks # use_frameworks! # Pods for Unity-iPhone target 'Unity-iPhone Tests' do inherit! :search_paths pod 'Google-Mobile-Ads-SDK', '7.36' #pod 'GoogleMobileAdsMediationMaio' #pod 'GoogleMobileAdsMediationUnity' pod 'AppLovinSDK', ' ~>6.11' pod 'AppLovinMediationMintegralAdapter' pod 'AppLovinMediationAmazonAdapter' pod 'AppLovinMediationByteDanceAdapter' pod 'AppLovinMediationFacebookAdapter' pod 'AppLovinMediationGoogleAdapter' pod 'AppLovinMediationTapjoyAdapter' pod 'AppLovinMediationUnityAdsAdapter' pod 'AppLovinMediationVungleAdapter' pod 'Firebase/Core' pod 'Firebase/Messaging' pod 'Firebase/Database' pod 'Firebase/Storage' pod 'Firebase/Auth' pod 'TenjinSDK' # Pods for testing end end |
長いPodfileで汚いですが参考までに記載します。
OnPostprocessBuildのEditorスクリプトファイル準備
ファイルはこちらからダウンロードできます。
BuildPostProcess.zip
zipを展開し、BuildPostProcess.csをEditorフォルダーに格納して下さい。
ポイントはOnPostProcessBuild()の以下の部分です(参考Blogそのまま利用させて頂きました!感謝)
(BuildPostProcess.csにはCocoaPodを利用しないframeworks追加方法も記載してあります)
1 2 3 |
// 4. Include Podfile into the project root folder. foreach (string podFilePath in PodFilePaths) CopyAndReplaceFile (podFilePath, Path.Combine (path, Path.GetFileName (podFilePath))); |
1 |
StartPodsProcess (path); |
CopyAndReplaceFileとStartPodProcessはどちらも内部でメソッドとして定義してあります。
処理としては、PodfileをUnity Cloud Build上で利用可能な場所にコピーし、pod installを実行しています。
フォルダーの階層設定などおかしい場合は、File Not Found系のエラーがUnity Cloud Buildで発生します。
以降にBuildPostProcess.csの全文を掲載します。直接今回の設定とは関係の無い部分もありますのでご参考までに。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
#if UNITY_IOS using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; using System.IO; public class BuildPostProcess { //このスクリプトを使うか private static bool useThis = true; //iOS9 SDK以上か? private static bool is_iOS9orOver = true; // cocoapodsに頼らないFramework追加 private static bool use_Nend = false; //nendを有効にするか private static bool useAdmob = true; //admobを有効にするか private static bool useSocial = true; //Socialconnectorなど // ビルド時に実行される [PostProcessBuild] public static void OnPostprocessBuild(BuildTarget buildTarget, string path) { if (buildTarget == BuildTarget.iOS) { if(!useThis){return;} string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj"; PBXProject proj = new PBXProject(); proj.ReadFromString(File.ReadAllText(projPath)); string target = proj.TargetGuidByName("Unity-iPhone"); string plistPath = Path.Combine (path, "Info.plist"); var plist = new PlistDocument (); plist.ReadFromFile (plistPath); string frameworkSuffix = (is_iOS9orOver) ? "tbd" : "dylib"; proj.AddFrameworkToProject(target, "iAd.framework", false); proj.AddFrameworkToProject(target, "UserNotifications.framework", false); if (useAdmob){ proj.AddFrameworkToProject(target, "CoreGraphics.framework", false); proj.AddFrameworkToProject(target, "EventKit.framework", false); proj.AddFrameworkToProject(target, "EventKitUI.framework", false); proj.AddFrameworkToProject(target, "MessageUI.framework", false); proj.AddFrameworkToProject(target, "StoreKit.framework", false); proj.AddFrameworkToProject(target, "Social.framework", false); } if(useSocial){ proj.AddFrameworkToProject(target, "MessageUI.framework", false); proj.AddFrameworkToProject(target, "Security.framework", false); proj.AddFrameworkToProject(target, "Social.framework", false); } //nend if (use_Nend) { proj.AddFrameworkToProject(target, "AdSupport.framework", false); proj.AddFrameworkToProject(target, "Security.framework", false); proj.AddFrameworkToProject(target, "ImageIO.framework", false); proj.AddFrameworkToProject(target, "AVFoundation.framework", false); proj.AddFrameworkToProject(target, "CoreMedia.framework", false); proj.AddFrameworkToProject(target, "SystemConfiguration.framework", false); proj.AddFrameworkToProject(target, "WebKit.framework", true); //Optional proj.AddFrameworkToProject(target, "CoreLocation.framework", false); proj.AddFrameworkToProject(target, "CoreMotion.framework", false); proj.AddFrameworkToProject(target, "CoreTelephony.framework", false); proj.AddFrameworkToProject(target, "AudioToolbox.framework", false); proj.AddBuildProperty(target, "HEADER_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks/NendAd/iOS/**"); } proj.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); // フレームワーク検索パスの設定 proj.AddBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(inherited)"); proj.AddBuildProperty(target, "HEADER_SEARCH_PATHS", "$(inherited)"); proj.AddBuildProperty(target, "OTHER_CFLAGS", "$(inherited)"); proj.AddBuildProperty(target, "OTHER_LDFLAGS", "$(inherited)"); // Frameworks/Plugins/iOS/**/*.framework // Use ** to make search setting in Xcode recursive proj.AddBuildProperty(target, "FRAMEWORK_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks/Plugins/iOS/**"); proj.AddBuildProperty(target, "HEADER_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks"); proj.AddBuildProperty(target, "HEADER_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks/Plugins/iOS/**"); //エクスポート時に文字列のパスが加えられること(Unity由来の事象)への対応 proj.UpdateBuildProperty (target, "HEADER_SEARCH_PATHS", new string[]{"$(SRCROOT)/Classes", "$(SRCROOT)"}, new string[]{"\"$(SRCROOT)/Classes\"", "\"$(SRCROOT)\""}); proj.UpdateBuildProperty (target, "LIBRARY_SEARCH_PATHS", new string[]{"$(SRCROOT)/Classes", "$(SRCROOT)"}, new string[]{"\"$(SRCROOT)/Classes\"", "\"$(SRCROOT)\""}); // Set a custom link flag proj.AddBuildProperty(target, "OTHER_LDFLAGS", "-ObjC"); proj.AddBuildProperty(target, "OTHER_LDFLAGS", "-fobjc-arc"); // 4. Include Podfile into the project root folder. foreach (string podFilePath in PodFilePaths) CopyAndReplaceFile(podFilePath, Path.Combine(path, Path.GetFileName(podFilePath))); File.WriteAllText(projPath, proj.WriteToString()); plist.WriteToFile (plistPath); // // Comment the 'StartPodsProcess ()' method call and build the project again in case you receive linker errors // when building the Xcode project, this may happen due to a unity bug when building for ARM64 architecture. // Note that in this case you'll need to invoke `pods.command` manually (just double click it on Finder). StartPodsProcess (path); } } #region Private methods internal static void CopyAndReplaceFile(string srcPath, string dstPath) { if (File.Exists(dstPath)) File.Delete(dstPath); File.Copy(srcPath, dstPath); } internal static void CopyAndReplaceDirectory(string srcPath, string dstPath) { if (Directory.Exists(dstPath)) Directory.Delete(dstPath); if (File.Exists(dstPath)) File.Delete(dstPath); Directory.CreateDirectory(dstPath); foreach (var file in Directory.GetFiles(srcPath)) File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file))); foreach (var dir in Directory.GetDirectories(srcPath)) CopyAndReplaceDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir))); } internal static void CopyDirectory(string srcPath, string dstPath) { if (!Directory.Exists(dstPath)) Directory.CreateDirectory(dstPath); foreach (var file in Directory.GetFiles(srcPath)) File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file))); foreach (var dir in Directory.GetDirectories(srcPath)) CopyAndReplaceDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir))); } static void StartPodsProcess(string path) { var proc = new System.Diagnostics.Process(); proc.StartInfo.FileName = Path.Combine(path, OpenPodsFileName); proc.Start(); } #endregion #region Paths static string[] PodFilePaths { get { return new[] { Path.Combine (PodFolderPath, "Podfile"), Path.Combine (PodFolderPath, "pods.command"), Path.Combine (PodFolderPath, OpenPodsFileName) }; } } static string OpenPodsFileName { get { return "open_pods.command"; } } static string PodFolderPath { get { return Path.Combine(XCodeFilesFolderPath, "Pod/"); } } static string StringsFolderPath { get { return Path.Combine(XCodeFilesFolderPath, "Strings/"); } } static string XCodeFilesFolderPath { get { return Path.Combine(UnityProjectRootFolder, "XcodeFiles/"); } } static string UnityProjectRootFolder { get { return "."; } } #endregion } #endif |
まとめ
Cocoapodsを利用しなくても、記載したファイルの様に直接Frameworkを追加することも出来ます。
今回利用しているPodfileもそうですが、利用しているSDKが増えてくると手動で管理するのも大変です。
実際普段からCocoaPodsを利用していると、Unity Cloud Buildでも使えないと辛いです。
日本語はともかく英語サイトでもあまり情報がでてこず、本当に使えるのか疑ってすみませんでした。
また、今回のBuildPostProcessはほとんど参考ブログからの丸パクリで成り立っています。
感謝しかありません。