Skip to content

Commit 778a484

Browse files
authored
Unity Editor support for tvOS pods and xcode projects (#478)
Updates to the Unity build process to handle tvOS pod dependencies and xcode project file configuration.
1 parent 5639e6d commit 778a484

File tree

10 files changed

+125
-49
lines changed

10 files changed

+125
-49
lines changed

app/platform/Unity/PlatformInformation.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ internal static bool IsAndroid {
2828
}
2929
}
3030

31-
// Is the current platform iOS?
31+
// Is the current platform iOS or tvOS?
3232
internal static bool IsIOS {
3333
get {
34-
return UnityEngine.Application.platform == UnityEngine.RuntimePlatform.IPhonePlayer;
34+
return UnityEngine.Application.platform == UnityEngine.RuntimePlatform.IPhonePlayer ||
35+
UnityEngine.Application.platform == UnityEngine.RuntimePlatform.tvOS;
3536
}
3637
}
3738

app/src/ErrorMessages.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal class ErrorMessages {
4343
"* Rebuild your APK and deploy.\n";
4444

4545
private static string DEPENDENCY_NOT_FOUND_ERROR_IOS =
46-
"On iOS, Firebase requires native (C/C++) and Cocoapod components\n" +
46+
"On iOS+ Firebase requires native (C/C++) and Cocoapod components\n" +
4747
"that are distributed with the Firebase SDK and via Cocoapods.\n" +
4848
"\n" +
4949
"It's likely that you did not include the require Cocoapod\n" +

editor/app/src/DocRef.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editor/app/src/GenerateXmlFromGoogleServicesJson.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//
2121
// It also creates the google-services-desktop.json file, (used when building on
2222
// desktop or running in the editor) based on either the google-services.json or
23-
// GoogleService-Info.plist file, depending on your current build target. (iOS
23+
// GoogleService-Info.plist file, depending on your current build target. (iOS+
2424
// targets will prefer the GoogleService-Info.plist file, while Android build
2525
// targets will prefer google-services.json.)
2626
//
@@ -172,7 +172,7 @@ private static void UpdateJson(bool ignoreModificationDate,
172172
// file.
173173

174174
// If the project is set to build for Android, the google-services.json file
175-
// is preferred. If it is set to build for iOS, the GoogleServices-Info.plist
175+
// is preferred. If it is set to build for iOS+, the GoogleServices-Info.plist
176176
// file is preferred. When set to standalone desktop, it tries to match the
177177
// current bundleid, and if it can't, it takes any plist/json file it can find.
178178

@@ -201,7 +201,8 @@ private static void UpdateJson(bool ignoreModificationDate,
201201
// Regenerate google-services-desktop file.
202202
switch (EditorUserBuildSettings.selectedBuildTargetGroup) {
203203
case BuildTargetGroup.iOS:
204-
// If we're on iOS, generate the desktop file from the plist, if possible.
204+
case BuildTargetGroup.tvOS:
205+
// If we're on iOS+, generate the desktop file from the plist, if possible.
205206
if (googleServiceInfoFile != null) {
206207
CreateDesktopJsonFromPlist(googleServiceInfoFile);
207208
} else if (googleServicesFile != null) {
@@ -229,7 +230,8 @@ private static void UpdateJson(bool ignoreModificationDate,
229230
logMessageForNoConfigFiles(
230231
String.Format(DocRef.GoogleServicesFileBundleIdMissing,
231232
GetAndroidApplicationId(),
232-
EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.iOS ?
233+
(EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.iOS ||
234+
EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.tvOS) ?
233235
"GoogleService-Info.plist" : "google-services.json",
234236
String.Join("\n", BundleIdsFromBundleIdsByConfigFile(
235237
ConfigFileDirectory).ToArray()),
@@ -465,10 +467,11 @@ private static void UpdateJsonWithBundleIdChooserDialog(string bundleId,
465467
ConfigFileType fileType;
466468
if (EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.Android) {
467469
fileType = ConfigFileType.Json;
468-
} else if (EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.iOS) {
470+
} else if (EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.iOS ||
471+
EditorUserBuildSettings.selectedBuildTargetGroup == BuildTargetGroup.tvOS) {
469472
fileType = ConfigFileType.Plist;
470473
} else {
471-
// Don't need to be here, if we're not on iOS or Android.
474+
// Don't need to be here, if we're not on iOS+ or Android.
472475
return;
473476
}
474477
var configFile = FindGoogleServicesFile(fileType, bundleId,

editor/app/src/XcodeProjectModifier.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,8 @@ private static bool ContainsUrlScheme(object inputTypes, string scheme) {
203203
// the methods. Once that is complete it saves the modifications back over the original files.
204204
[PostProcessBuild]
205205
internal static void PostProcessBuild(BuildTarget buildTarget, string pathToBuiltProject) {
206-
// BuiltTarget.iOS is not defined in Unity 4, so we just use strings here
207-
if (buildTarget.ToString() != "iOS" && buildTarget.ToString() != "iPhone") {
208-
logger.LogDebug("Skipping PostProcessBuild as target {0} is not for iOS", buildTarget);
206+
if (buildTarget != BuildTarget.iOS && buildTarget != BuildTarget.tvOS) {
207+
logger.LogDebug("Skipping PostProcessBuild as target {0} is not for iOS+", buildTarget);
209208
return;
210209
}
211210

editor/app/src/XcodeProjectPatcher.cs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,23 @@ internal class XcodeProjectPatcher : AssetPostprocessor {
6464
private static bool Enabled {
6565
get {
6666
return (EditorUserBuildSettings.activeBuildTarget ==
67-
BuildTarget.iOS) && Google.IOSResolver.Enabled;
67+
BuildTarget.iOS ||
68+
EditorUserBuildSettings.activeBuildTarget ==
69+
BuildTarget.tvOS) && Google.IOSResolver.Enabled;
6870
}
6971
}
7072

7173
static XcodeProjectPatcher() {
72-
// Delay initialization until the build target is iOS and the editor is not in play
73-
// mode.
74+
// Delay initialization until the build target is iOS+ and the
75+
// editor is not in play mode.
7476
EditorInitializer.InitializeOnMainThread(
7577
condition: () => {
76-
return EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS &&
78+
return (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS ||
79+
EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS) &&
7780
!EditorApplication.isPlayingOrWillChangePlaymode;
7881
}, initializer: () => {
7982
// We attempt to read the config even when the target platform isn't
80-
// iOS as the project settings are surfaced in the settings window.
83+
// iOS+ as the project settings are surfaced in the settings window.
8184
Google.IOSResolver.RemapXcodeExtension();
8285
ReadConfigOnUpdate();
8386
PlayServicesResolver.BundleIdChanged -= OnBundleIdChanged;
@@ -96,15 +99,19 @@ internal static void ReadConfigOnUpdate() {
9699
ReadConfig(errorOnNoConfig: false);
97100
}
98101

99-
// Get the iOS bundle / application ID.
100-
private static string GetIOSApplicationId() {
101-
return UnityCompat.GetApplicationId(BuildTarget.iOS);
102+
// Get the iOS+ bundle / application ID.
103+
private static string GetIosPlusApplicationId() {
104+
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS) {
105+
return UnityCompat.GetApplicationId(BuildTarget.tvOS);
106+
} else {
107+
return UnityCompat.GetApplicationId(BuildTarget.iOS);
108+
}
102109
}
103110

104111
// Check the editor environment on the first update after loading this
105112
// module.
106113
private static void CheckConfiguration() {
107-
CheckBundleId(GetIOSApplicationId());
114+
CheckBundleId(GetIosPlusApplicationId());
108115
CheckBuildEnvironment();
109116
}
110117

@@ -115,9 +122,9 @@ internal static void ReadConfig(bool errorOnNoConfig = true, string filename = n
115122
ReadConfigInternal(errorOnNoConfig, filename: filename);
116123
} catch (Exception exception) {
117124
// FileNotFoundException and TypeInitializationException can be
118-
// thrown *before* ReadConfigInternal is entered if the iOS Xcode
125+
// thrown *before* ReadConfigInternal is entered if the iOS+ Xcode
119126
// assembly can't be loaded so we catch them here and only report
120-
// a warning if this module is enabled and iOS is the selected
127+
// a warning if this module is enabled and iOS+ is the selected
121128
// platform.
122129
if (exception is FileNotFoundException ||
123130
exception is TypeInitializationException) {
@@ -158,11 +165,13 @@ internal static Dictionary<string, string> GetConfig() {
158165
return configValues;
159166
}
160167

161-
// Verify that the build environment *really* supports iOS.
168+
// Verify that the build environment *really* supports iOS+.
162169
private static void CheckBuildEnvironment() {
163-
// If iOS is the selected build target but we're not on a OSX machine
164-
// report an error as pod installation will fail among other things.
165-
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS &&
170+
// If iOS+ is the selected build target but we're not on a OSX
171+
// machine report an error as pod installation will fail among other
172+
// things.
173+
BuildTarget buildTarget = EditorUserBuildSettings.activeBuildTarget;
174+
if ((buildTarget == BuildTarget.iOS || buildTarget == BuildTarget.tvOS) &&
166175
Application.platform == RuntimePlatform.WindowsEditor) {
167176
Debug.LogError(DocRef.IOSNotSupportedOnWindows);
168177
}
@@ -173,7 +182,7 @@ private static void OnBundleIdChanged(
173182
object sender,
174183
PlayServicesResolver.BundleIdChangedEventArgs args) {
175184
ReadConfig(errorOnNoConfig: false);
176-
CheckBundleId(GetIOSApplicationId());
185+
CheckBundleId(GetIosPlusApplicationId());
177186
}
178187

179188
// Check the bundle ID
@@ -213,10 +222,16 @@ private static string CheckBundleId(string bundleId,
213222
"Cancel",
214223
selectedBundleId => {
215224
if (!String.IsNullOrEmpty(selectedBundleId)) {
216-
// If we have a valid value, the user hit apply.
217-
UnityCompat.SetApplicationId(BuildTarget.iOS, selectedBundleId);
218-
Measurement.ReportWithBuildTarget("bundleidmismatch/apply", null,
219-
"Mismatched Bundle ID: Apply");
225+
switch(EditorUserBuildSettings.activeBuildTarget) {
226+
case BuildTarget.iOS:
227+
UnityCompat.SetApplicationId(BuildTarget.iOS, selectedBundleId);
228+
break;
229+
case BuildTarget.tvOS:
230+
UnityCompat.SetApplicationId(BuildTarget.tvOS, selectedBundleId);
231+
break;
232+
default:
233+
throw new Exception("unsupported iOS+ version");
234+
}
220235
} else {
221236
Measurement.ReportWithBuildTarget("bundleidmismatch/cancel", null,
222237
"Mismatched Bundle ID: Cancel");
@@ -238,7 +253,7 @@ private static string CheckBundleId(string bundleId,
238253
private static void OnPostprocessAllAssets(
239254
string[] importedAssets, string[] deletedAssets,
240255
string[] movedAssets, string[] movedFromPath) {
241-
// We track the config file state even when the target isn't iOS
256+
// We track the config file state even when the target isn't iOS+
242257
// as the project settings are surfaced in the settings window.
243258
if (!Enabled) return;
244259
bool configFilePresent = false;
@@ -251,7 +266,7 @@ private static void OnPostprocessAllAssets(
251266
if (configFilePresent) {
252267
spamguard = false; // Reset our spamguard to show a dialog.
253268
ReadConfig(errorOnNoConfig: false);
254-
CheckBundleId(GetIOSApplicationId());
269+
CheckBundleId(GetIosPlusApplicationId());
255270
}
256271
}
257272

@@ -283,7 +298,7 @@ internal static string FindConfig(bool errorOnNoConfig = true) {
283298
GOOGLE_SERVICES_INFO_PLIST_FILE, Link.IOSAddApp));
284299
}
285300
} else if (files.Length > 1) {
286-
var bundleId = GetIOSApplicationId();
301+
var bundleId = GetIosPlusApplicationId();
287302
string selectedBundleId = null;
288303
// Search files for the first file matching the project's bundle identifier.
289304
foreach (var filename in files) {
@@ -314,7 +329,9 @@ internal static string FindConfig(bool errorOnNoConfig = true) {
314329
internal static void OnPostProcessAddGoogleServicePlist(
315330
BuildTarget buildTarget, string pathToBuiltProject) {
316331
if (!Enabled) return;
317-
Measurement.analytics.Report("ios/xcodepatch", "iOS Xcode Project Patcher: Start");
332+
string platform = (buildTarget == BuildTarget.iOS) ? "iOS" : "tvOS";
333+
Measurement.analytics.Report("ios/xcodepatch",
334+
platform + " Xcode Project Patcher: Start");
318335
AddGoogleServicePlist(buildTarget, pathToBuiltProject);
319336
}
320337

@@ -331,7 +348,7 @@ internal static void AddGoogleServicePlist(
331348
return;
332349
}
333350

334-
CheckBundleId(GetIOSApplicationId(), promptUpdate: false);
351+
CheckBundleId(GetIosPlusApplicationId(), promptUpdate: false);
335352

336353
// Copy the config file to the Xcode project folder.
337354
string configFileBasename = Path.GetFileName(configFile);

editor/crashlytics/src/iOSPostBuild.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,19 @@ public class iOSPostBuild {
6868
/// </param>
6969
[PostProcessBuild(100)]
7070
public static void OnPostprocessBuild(BuildTarget buildTarget, string buildPath) {
71-
// BuiltTarget.iOS is not defined in Unity 4, so we just use strings here
72-
if (buildTarget.ToString() == "iOS" || buildTarget.ToString() == "iPhone") {
71+
if (buildTarget == BuildTarget.iOS || buildTarget == BuildTarget.tvOS) {
7372
string projectPath = Path.Combine(buildPath, "Unity-iPhone.xcodeproj/project.pbxproj");
7473
string plistPath = Path.Combine(buildPath, "Info.plist");
7574

7675
IFirebaseConfigurationStorage configurationStorage = StorageProvider.ConfigurationStorage;
7776

78-
PrepareProject(projectPath, configurationStorage);
77+
PrepareProject(projectPath, configurationStorage, buildTarget);
7978
AddCrashlyticsDevelopmentPlatformToPlist(plistPath);
8079
}
8180
}
8281

83-
private static void PrepareProject(string projectPath, IFirebaseConfigurationStorage configurationStorage) {
82+
private static void PrepareProject(string projectPath, IFirebaseConfigurationStorage configurationStorage,
83+
BuildTarget buildTarget) {
8484
// Return if we have already added the script
8585
var xcodeProjectLines = File.ReadAllLines(projectPath);
8686
foreach (var line in xcodeProjectLines) {
@@ -96,7 +96,7 @@ private static void PrepareProject(string projectPath, IFirebaseConfigurationSto
9696

9797
string completeRunScriptBody = GetRunScriptBody(configurationStorage);
9898

99-
string appGUID = iOSPostBuild.GetMainUnityProjectTargetGuid(pbxProject);
99+
string appGUID = iOSPostBuild.GetMainUnityProjectTargetGuid(pbxProject, buildTarget);
100100
SetupGUIDForSymbolUploads(pbxProject, completeRunScriptBody, appGUID);
101101

102102
// In older versions of Unity there is no separate framework GUID, so this can
@@ -183,7 +183,7 @@ private static string GetUnityFrameworkTargetGuid(object projectObj) {
183183
/// </summary>
184184
/// <param name="projectObj">The project where the main target GUID will be read from</param>
185185
/// <returns>The main target GUID</returns>
186-
private static string GetMainUnityProjectTargetGuid(object projectObj) {
186+
private static string GetMainUnityProjectTargetGuid(object projectObj, BuildTarget buildTarget) {
187187
var project = (UnityEditor.iOS.Xcode.PBXProject)projectObj;
188188
MethodInfo getUnityMainTargetGuid = project.GetType().GetMethod("GetUnityMainTargetGuid");
189189

@@ -194,7 +194,7 @@ private static string GetMainUnityProjectTargetGuid(object projectObj) {
194194
} else {
195195
// Hardcode the main target name "Unity-iPhone" by default, just in case
196196
// GetUnityTargetName() is not available.
197-
string targetName = "Unity-iPhone";
197+
string targetName = (buildTarget == BuildTarget.tvOS) ? "Unity-tvOS" : "Unity-iPhone";
198198
MethodInfo getUnityTargetName = project.GetType().GetMethod("GetUnityTargetName");
199199
if (getUnityTargetName != null) {
200200
targetName = (string) getUnityTargetName.Invoke(null, new object[] {});

firestore/testapp/Assets/Firebase/Editor/Builder.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ public static void BuildIos() {
4343
Build(options);
4444
}
4545

46+
public static void BuildTvos() {
47+
var options = new BuildPlayerOptions();
48+
49+
// For tvOS, this is the name of the folder containing the generated XCode
50+
// project.
51+
options.locationPathName = "tvos-build";
52+
options.target = BuildTarget.tvOS;
53+
// Firebase Unity plugins don't seem to work on a simulator.
54+
PlayerSettings.tvOS.sdkVersion = tvOSSdkVersion.DeviceSDK;
55+
56+
// AcceptExternalModificationsToPlayer corresponds to "Append" in the Unity
57+
// UI -- it allows doing incremental builds.
58+
options.options = BuildOptions.AcceptExternalModificationsToPlayer;
59+
60+
Build(options);
61+
}
62+
4663
public static void BuildAndroid() {
4764
var options = new BuildPlayerOptions();
4865

0 commit comments

Comments
 (0)