Wednesday, September 15, 2021

Flynn: Son of Crimson!

A charming and beautiful platform game built on GameMaker, Flynn is now available on Switch, PS4, Xbox, and Steam!  We worked with Humble and Studio Thunderhorse to bring the title to all three console platforms.  Explore Rosantica with your trusty dog, Dex, by your side.  Push back the scourge and discover the truth behind what threatens your home!


Friday, September 3, 2021

Fixing Invalid Frameworks Folder in iOS App Store Upload

Upon moving a couple of recent projects to 2020.4, we learned that recent versions of Unity apparently have problems when exporting iOS applications.  They’ll run and test fine, but upon uploading to the App Store, you’ll get an error.  This reads:

Invalid Bundle. The bundle at 'my.app/Frameworks/UnityFramework.framework' contains disallowed file 'Frameworks'.

We spent a long while trying suggestions on forum threads and reviewing the build behavior before we came up with a solution that works for us.  This also handles a further error we got after submitting the iOS app, which reads:

Invalid Swift Support - The SwiftSupport folder is empty. Rebuild your app using the current public (GM) version of Xcode and resubmit it.

You can use the solution we created by saving the below code out to a file called UpdateXcodeBuildSystemPostProcessor.cs and putting it in your project’s assets/editor folder.

using System.IO;

using UnityEngine;

using UnityEditor;

using UnityEditor.Callbacks;

using UnityEditor.iOS.Xcode;


#if UNITY_IOS || UNITY_TVOS

// Create specific aliases for iOS.Xcode imports.

// Unity Editor on macOS can report a conflict with other plugins

using PlistDocument = UnityEditor.iOS.Xcode.PlistDocument;

using PlistElementDict = UnityEditor.iOS.Xcode.PlistElementDict;

#endif


public class UpdateXcodeBuildSystemPostProcessor : MonoBehaviour

{

    [PostProcessBuild(999)]

    public static void OnPostProcessBuild(BuildTarget buildTarget, string path)

    {

        if (buildTarget == BuildTarget.iOS || buildTarget == BuildTarget.tvOS)

        {

            UpdateXcodeBuildSystem(path);

        }

    }


    private static void UpdateXcodeBuildSystem(string projectPath)

    {

        string workspaceSettingsPath = Path.Combine(projectPath,

            "Unity-iPhone.xcodeproj/project.xcworkspace/xcshareddata/" +

            "WorkspaceSettings.xcsettings");


        if (File.Exists(workspaceSettingsPath))

        {

            // Read the plist document, and find the root element

            PlistDocument workspaceSettings = new PlistDocument();

            workspaceSettings.ReadFromFile(workspaceSettingsPath);

            PlistElementDict root = workspaceSettings.root;


            // Modify the document as necessary.

            bool workspaceSettingsChanged = false;

            // Remove the BuildSystemType entry because it specifies the

            // legacy Xcode build system, which is deprecated


            if (root.values.ContainsKey("BuildSystemType"))

            {

                root.values.Remove("BuildSystemType");

                workspaceSettingsChanged = true;

            }


            // If actual changes to the document occurred, write the result

            // back to disk.

            if (workspaceSettingsChanged)

            {

                Debug.Log("UpdateXcodeBuildSystem: Writing updated " +

                    "workspace settings to disk.");


                try

                {

                    workspaceSettings.WriteToFile(workspaceSettingsPath);

                }

                catch (System.Exception e)

                {

                    Debug.LogError(string.Format("UpdateXcodeBuildSystem: " +

                        "Exception occurred writing workspace settings to " +

                        "disk: \n{0}",

                        e.Message));

                    throw;

                }

            }

            else

            {

                Debug.Log("UpdateXcodeBuildSystem: workspace settings did " +

                    "not require modifications.");

            }

        }

        else

        {

            Debug.LogWarningFormat("UpdateXcodeBuildSystem: could not find " +

                "workspace settings files [{0}]",

                workspaceSettingsPath);

        }


        // Get the path to the Xcode project

        string pbxProjectPath = PBXProject.GetPBXProjectPath(projectPath);

        var pbxProject = new PBXProject();


        // Open the Xcode project

        pbxProject.ReadFromFile(pbxProjectPath);


        // Get the UnityFramework target GUID

        string unityFrameworkTargetGuid =

            pbxProject.GetUnityFrameworkTargetGuid();


        // Modify the Swift version in the UnityFramework target to a

        // compatible string

        pbxProject.SetBuildProperty(unityFrameworkTargetGuid,

            "SWIFT_VERSION", "5.0");


        // Write out the Xcode project

        pbxProject.WriteToFile(pbxProjectPath);


        Debug.Log("UpdateXcodeBuildSystem: update Swift version in Xcode " +

            "project.");

    }

}

We do two things here:

  1. Unity writes out the Xcode project using Xcode’s “Legacy Build System”, which is currently deprecated in Xcode 12.5.1 where this was tested.  We remove the “BuildSystemType” key from the Xcode workspace settings so that Xcode uses the default build system.
  2. Once Xcode is using the default build system, it no longer recognizes the format of the Swift version that Unity writes out in the Xcode project.  Unity writes something like “5 0 5 1”, but Xcode expects “5.0”.  So we forcibly set the Swift version to a value Xcode currently understands.
You could do this manually.  After making a Unity build for iOS, you would open the Xcode project, go to the File menu, select Project Settings, and change the Build System in the drop-down menu there.  Changing this through Xcode will automatically fix the Swift version value.

Thanks to everyone on the various Unity forum threads who have been wrestling with this problem.

Thanks also to user uannoymejo on this forum thread for bringing up the Xcode build system.

Wednesday, September 1, 2021

Apsulov: End of Gods

Apsulov is a stunning action-horror exploration of Norse mythology, now available on the PS4 and Switch as either physical disk or digital download.  Creep through dark sci-fi hallways as you explore the worlds of Yggdrasil, evade horrific creatures, and understand what brought you to this place.  We worked with Digerati to bring this Unreal-based title to all three consoles.

Thursday, April 15, 2021

Darkside Detective!

Do you want to track down deeper and darker mysteries of ever-increasing spookiness?  Darkside Detective is a tongue-in-cheek horror-inspired classic adventure, and we worked with Akupara to bring both episodes to all three consoles!


Check out the web site here!

Tuesday, March 9, 2021

Provisioning MacOS Big Sur Devices

 When you need to set up one of your brand new macOS Big Sur computers as a test device (as described in "Register Test Devices" in a previous post), be aware that Apple has changed the required value for current devices.  When you visit System Report -> Hardware, there are now two entries in the Hardware Overview: Hardware UDID and Provisioning UDID.  You must use the Provisioning UDID when you register your device on your developer device list.  For Macs that have been upgraded to Big Sur, these two IDs are probably the same.  But on a fresh new Big Sur device, they most likely will not be the same.

If you try to use the incorrect Hardware ID, you will probably get a cryptic error message saying something like "You do not have permission to open the application".  (See this StackOverflow post.)  It's non-obvious, but if you look at the console for the system log, you might see a message saying "Provisioning Profile Validation: profile 'XXXXX' is not provisioned for this device (<private>)".  That's a good indication that you've used the wrong UDID.

Friday, February 19, 2021

Signing & Notarizing a MacOS Gamemaker Application

So you need to manually sign and notarize a MacOS application, and it isn’t working out of the box.  Here’s the steps that worked for me, in particular for a Gamemaker VM build of a project.  Obviously this could be automated via a shell script, if you so desire.

First, manually sign the application. Using --force and --deep make it replace and be recursive, both of which we want here.  Having --timestamp is required for notarization, and was not present in Gamemaker's default signing.  Using options=runtime will set it to use hardened runtime, which is also required for notarization.  Note that the certificate you sign with has to be a Developer ID Application and you MUST have the private key locally.  If you can't open the key listing and see someone's name underneath in the keychain, don’t expect it to work because that’s only a public key.


You’ll want to have an entitlements file.  This needs to contain at least the com.apple.security.automation.apple-events if you're doing a hardened runtime, and past experience has shown me that bluetooth and USB access are generally necessary for gamepad.  An example entitlements file is here, though those bottom few may not all be needed depending on your game and engine.

codesign --sign "Developer ID Application: COMPANY NAME, LLC" "Your Game.app" --force --deep --timestamp --options=runtime --entitlements game.entitlements

After this is done, zip the app.  I read in more than one place to use this command-line rather than the right-click menu’s “compress” feature, though I’m not sure that’s really true.  Still, easy enough.

/usr/bin/ditto -c -k --keepParent "Your Game.app" YourGameBuildMacSteam_Resigned.zip

Now that you’ve signed the build and made a new archive of that, you’ll need to submit it for notarization.  Note that asc-provider should match the organization of the Developer ID application in the first step, and I’ve obfuscated the one I used.  The password is for an app-specific password, which you probably have around but you’ll need to create one if not.  Finally, you can add the password to your keychain with an alias so it doesn't end up in the command-line directly, which is what I did here.  That’s important if you want to write a shell script for these steps and check it in.

xcrun altool --notarize-app --primary-bundle-id "com.company.yourgame" --username flast@company.com --password "@keychain:APP_PASSWORD" --asc-provider J93FFYZ67E --file YourGameMacSteam_Resigned.zip

This command will output a randomly generated ID, keep that around.  Now you wait.


If things go well, you’ll get an e-mail from Apple after a while saying notarization is a success.  If notarization succeeds, you'll need to staple the result to the app.  Note that this is the app, not the zip.  I assume it figures out what to do based on the signature.

xcrun stapler staple "Your Game.app"

At this point, you should be able to upload your application to Steam, or zip it (remember, your existing zip is now out of date) and distribute it.


If notarization failed, the e-mail from Apple will say as much.  You can use altool to ask for reasons of the failure, using that randomly generated ID that you hopefully kept around.

xcrun altool --notarization-info fdeb1abc-25ae-43f1-8bca-97b89078196c --username flast@company.com --password "@keychain:APP_PASSWORD”

Assuming the process is completed, this will give you a URL you can paste into a web browser.  Sometimes the listed reasons are clear and other times not. If the process isn’t yet complete, it’ll say so.