Notifications
Article
Building for the OSX App Store from Unity’s Window Editor
Published 2 years ago
289
0
The Nevermind Team’s Approach to Cross-Platform Building for the OSX App Store

Introduction

 
One of Unity’s key features is its cross-platform support. While there are nuances to supporting different platforms and storefronts, developers can trust that most of the core functionality and visuals of a Unity game will not change between platforms. As the Lead Engineer of Flying Mollusk, I have taken advantage of this feature to enable three variations of our game, Nevermind, since our launch on the Steam store: Windows 32-bit, Windows 64-bit, and Mac OSX 64-bit.
With a recent update, however, we’ve added a new, fourth variation. A Mac OSX 64-bit version meant to be distributed through the OSX App Store and integrate with Apple’s GameCenter achievements in place of the Steam achievements. Now, distributing a game through Steam and the OSX App Store at the same time is, by itself, not groundbreaking. Other games have done it before us and other developers will accomplish the same feat in the future.
The road we took to accomplish this, however, wasn’t a straightforward path. It took a lot of research and experimentation to construct our current build pipeline and explaining that is the purpose behind this Made With Unity article. We want to share what we learned so that other developers can be spared some of the blood, sweat, and tears we endured to get our build process where it is today.
I’d also like to say this before we get into the nitty-gritty. This article is meant to help other developers make an OSX App from Unity’s Windows Editor that can pass the strict signing and submission guidelines of the OSX App store. If you’re just planning to release the OSX version of your game through a platform like Steam, then Unity’s standard build process is usually more than sufficient.

The Situation

 
As the subtitle indicates, the primary topic of this article is how someone can make a build from Unity’s Windows Editor that will pass the strict submission guidelines of the OSX App Store. But the first question that should be addressed is “why.” Why aren’t we just making our OSX App Store build with Unity’s OSX Editor? Surely that is the simpler path.
There are a few reasons why, the biggest of which is that we’re a primarily Windows-based development studio. At Flying Mollusk, almost all of our development machines are Windows-based. Many of the technologies we integrated into Nevermind, like Intel’s RealSense Camera and Tobii’s EyeX Tracker, are Windows-based SDK’s. Furthermore, the studio has only one dedicated OSX machine - a Mac Mini that doesn’t like us running the Unity editor on it for any significant length of time.
Beyond the hardware limitations, we also had our existing build process to consider as well. Thanks to the fact that Unity allows for custom editor scripts, I was able to write an automated build process. With a few button presses, the Unity editor compiles all the variations of Nevermind we currently support from a single project folder. Thus, the ideal solution for us to support an OSX App Store variation of Nevermind was to integrate it into the existing automated build process.
It was a challenge, but one we were, thankfully, able to solve.
So now that we’ve gone over the why, we can start getting into the details of how. Our discussions on the process of making an OSX App Store build from Unity’s Windows Editor will be broken into two sections. The first section will focus on the steps that have to be taken on the Windows build machine.
The second section will then go over what must be done on an accompanying OSX machine. Yes, you still have to have an OSX machine. Even if we were able to do everything we needed to the OSX build on a Windows machine, we’d still need an OSX computer to make use of Apple’s Application Loader (although it is not necessary to have Unity installed on that OSX machine). Still, our process made it so that we don’t need to have Unity open on our Mac Mini. Everything can be done through a nice, clean command file. (A command file is OSX’s rough equivalent to a Windows-based batch file.)
Lastly, the process described here was established in Spring of 2016. If you are reading it well after that date, changes to the Unity editor or Apple’s application submission process could result in some steps being invalidated.

What Happens on Windows

 
First, for a Windows build, you want to go to your PlayerSettings and make sure “Mac App Store Validation” is unchecked. This may seem a bit counterintuitive, but that’s because the process we’re about to go through is meant to replace the internal processes Unity tries to go through for a Mac App Store submission - a process that are only really meant work on an OSX machine.
After that, we need to do some post-processing on the resulting OSX app once the compilation process is complete. These steps could be done manually or can be automated using Unity editor scripting. I am personally a strong advocate of writing the necessary scripts to automate the process to minimize errors and ensure build consistency.
If you’ve never done build post-processing via scripting, check out these following resources:
  • http://docs.unity3d.com/Manual/BuildPlayerPipeline.html
  • http://docs.unity3d.com/ScriptReference/Callbacks.PostProcessBuildAttribute.html
Our first focus when post-processing the built OSX App will be the Info.plist files. Your app and any .bundle OSX plugins you use will have an associated Info.plist file. This file contains metadata about your app/bundle that is checked, validated, and used by Apple’s servers for a number of purposes. But don’t be too afraid of this strange file. The Info.plist file is an XML formatted text file that is easy enough to modify by hand or through an automated process.
Your OSX App will have one primary Info.plist file located at YourGame.app/Contents/Info.plist, and we’ll focus on that file first. There are a few modifications that must be made to the primary Info.plist if it is going to be accepted by the automatic application validation process run by Apple’s servers whenever you upload a build.
The first modification we must make to the game’s primary Info.plist is to replace the default version numbers. Apple requires every app uploaded sent to their storefronts to have a unique and ever-increasing version number. Unity’s iOS PlayerSettings panel exposes a means of setting and updating the required version number through the editor. The OSX PlayerSettings panel, however, does not expose the same field.
Thus, we, as the developer, must update the version number in the primary Info.plist file. This should be done for two of the key/value pairs contained within the Info.plist. One is CFBundleShortVersionString, and the other is CFBundleVersion. If you want to know Apple’s specific expectations for these two plist entries, check their documentation here.
To use as an example, this is how the lines in the Info.plist should change if your version number would be 1.02.03:
 

ORIGINAL

<key>CFBundleVersion</key>
<string>5.3.4p1</string>
<key>CFBundleShortVersionString</key>
<string>Unity Player version 5.3.4p1</string>
 

MODIFIED

<key>CFBundleVersion</key>
<string>1.02.03</string>
<key>CFBundleShortVersionString</key>
<string>1.02.03</string>
 
 
The second modification, while important, is one that might be optional. Apple’s app stores, both for OSX and iOS, expect every app to have a unique bundle identifier. The common form for this bundle identifier is com.CompanyName.AppName. The Unity editor will attempt to assemble a valid bundle identifier for your game using the Company Name and Product Name fields from your PlayerSettings and will populate the result to the CFBundleIdentifier entry of the Info.plist.
If you can get the bundle identifier generated by Unity’s automatic process to match your game’s bundle identifier as it is displayed in iTunesConnect, then this step can be skipped. If, however, you want to be able to exactly define your own product identifier, then simply replace the CFBundleIdentifier field just as we replaced the version number fields in the previous step.
 

ORIGINAL

<key>CFBundleIdentifier</key>
<string>com.UnityDefault.Default</string>
 

MODIFIED

<key>CFBundleIdentifier</key>
<string>com.SuperCompany.SuperGame</string>
 
 
The third modification we’ll be making to the Info.plist is regarding copyright metadata. While this isn’t validated by the OSX App store automatic submission system, you should ensure this information accurately represents your game and your company as it displays on your app’s store page. We will be modifying the existing key/value pair for CFBundleGetInfoString. We’ll also be adding a new key called NSHumanReadableCopyright.
The reason we need to add NSHumanReadableCopyright is because CFBundleGetInfoString is technically deprecated. So, in theory, you could delete the CFBundleGetInfoString rather than modify it. We chose to modify it since there is no harm leaving it in the Info.plist and it’s an entry that is already created by Unity’s build process.
Here is an example of how you might modify/add the copyright metadata:
 

ORIGINAL

<key>CFBundleGetInfoString</key>
<string>Unity Player version 5.3.4p1 (e89f89413a91). (c) 2016 Unity Technologies ApS. All rights reserved.</string>
 

MODIFIED

<key>CFBundleGetInfoString</key>
<string>SuperGame version 1.02.03  (c) 2016 SuperCompany LLC. All rights reserved.</string>
<key>NSHumanReadableCopyright</key>
<string>SuperGame version 1.02.03  (c) 2016 SuperCompany LLC. All rights reserved.</string>
 
The fourth and final modification we’ll be making to the primary Info.plist is adding the key/value pair for LSApplicationCategoryType property. This is something the OSX App Store needs in order to properly categorize your app. If you’re making a game, you’ll likely be adding the following lines to your primary Info.plist file.
 

ADDED

<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
 
 
After adding the LSApplicationCategoryType key/value pair, we are done modifying the game’s primary Info.plist and can move onto validating the Info.plist files of any plugins your app employes.
Now, you may be asking, “Why would I need to modify my plugins’ Info.plist files?” The reason you need to is because the OSX automatic application upload validator doesn’t allow two things to be uploaded with the same CFBundleIdentifier. This makes sense for your app’s top level bundle identifier, but it also extends to every CFBundleIdentifier in your app, including the CFBundleIdentifier of any OSX plugins your application uses.
So, as an example, let’s say your SuperGame makes use of a plugin off the Unity Asset Store that lets you trigger OSX Popups. This plugin uses a .bundle to facilitate its functionality, and that .bundle has its own Info.plist.
If someone else has ever used that plugin before when uploading their Unity game to the OSX App Store, then the CFBundleIdentifier will likely get flagged by Apple’s automatic application validation process.
Thus, we have to change the plugin’s CFBundleIdentifier key/value pair. But how should we change it? The technical requirement is simple—it has to be entirely unique. At the same time, my personal opinion is that you should try to maintain as much of the original Bundle Identifier as possible.
The solution I used was to combine the plugin and app’s CFBundleIdentifier values into one that is almost always guaranteed to be unique. An example of this combination follows:
 

PLUGIN’S ORIGINAL INFO.PLIST

<key>CFBundleIdentifier</key>
<string>com.PluginCompany.OSXPopUpPlugin</string>
 

APP’S ORIGINAL INFO.PLIST

<key>CFBundleIdentifier</key>
<string>com.SuperCompany.SuperGame</string>
 

PLUGIN’S MODIFIED INFO.PLIST

<key>CFBundleIdentifier</key>
<string>com.PluginCompany.SuperCompany.SuperGame.OSXPopUpPlugin</string>
 
The result is, admittedly, long for a CFBundleIdentifier, but it is guaranteed to be unique since the app’s top-level CFBundleIdentifier is itself guaranteed to be unique. This method also respects the plugin’s original bundle identifier. In the end, however, you must simply ensure that any CFBundleIdentifier of plugins that your game uses are unique. How you ensure this uniqueness is your personal choice.
With all those modifications done, the only thing left to do on the Windows side is a little cleanup. If you are using .meta files to make your Unity project compatible with a version control solution, then there are a slew of .meta files that have crept in and polluted your OSX App’s plugins folder.
You may wonder why this happens on Windows and not on Mac. It’s because Windows treats the .bundles, and your .app, as regular folders where the OSX Editor respects their nature as self-contained packages.
Thankfully, it's easy enough to delete the .meta files that snuck into where they don’t belong. The .bundle folder itself doesn’t normally contain any files with a .meta extension. Thus, all you have to do is use the following lines of code, either in an Editor script or as a PostProcessBuildAttribute, command to search out and destroy all of the .meta files.
 
string[] files = System.IO.Directory.GetFiles(storedOSXBuildLocation + "/Contents/Plugins", 
"*.meta", 
SearchOption.AllDirectories);
foreach(string file in files)
{
          Try
          {
                   FileInfo fileInfo = new FileInfo(file);
                   File.Delete(fileInfo.FullName);
                   UnityEngine.Debug.Log("Mac App Store Build: Deleted: " +
                   fileInfo.FullName);
          }
          catch
         {
                  UnityEngine.Debug.Log ("Mac App Store Build: Failed to Delete:" +
                  file);
         }
}
 
Once the .meta files are cleaned out, we’ve finished all the post-processing steps that need to be done on the Windows machine that was used to compile the OSX App. Hopefully, you can also see why I suggest taking the extra effort to write code into your Unity editor to automate this process. There are a lot of steps, and many of them have to be redone each time you make a new OSX build.
Still, with all that said, we can now move onto the steps you’ll need to take on the OSX side.
 

What Happens on OSX

 
Congratulations on getting this far. At this point, you should try running your game on an OSX machine. Give it a good thorough testing, and once you're happy with its stability, you can begin the process of finalizing your game for the OSX app store.
To begin, please use the list below to ensure you have everything you need for the following steps.
  • Have latest XCode installed on your OSX machine
  • Using XCode, ensure you have the following two signing certificates installed
  • a.  3rd Party Mac Developer Application Certificate
  • b. 3rd Party Mac Developer Installer Certificate
  • Create a prep folder and name it something like MacAppStorePrep on your OSX machine.
  • Place a copy of your recently built OSX app into the MacAppStorePrep folder.
  • Download and place a copy of your apps Distribution Provisioning Profile in this prep folder.
  • Copy a file call the optool from your Window’s Unity Installation into the prep folder on OSX.
  • Optool is found at (UnityInstallDirectory/Editor/Data/PlaybackEngines/MacStandaloneSupport/optool)
With the checklist complete, we are well on our way to submitting an app successfully to the OSX App store.
The next thing we have to do is generate an accurate .entitlements file for our game. The OSX App Store requires that most new apps that are submitted to be sandboxed, at the very least. This is to protect the user's data while also giving your app a clean environment to work with. A very basic entitlements file looks something like this:
 

BASIC ENTITLEMENTS FILE

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
               <key>com.apple.security.app-sandbox</key> <true/>
</dict>
</plist>
 
 
You can add the necessary keys to the above basic .entitlements file based on what permissions your app needs. For example, if your game is going to connect to Apple’s GameCenter and is also an online multiplayer game, you’d likely need at least these two additional entitlements.
 

EXAMPLE ENTITLEMENTS FILE FOR GAMECENTER & MULTIPLAYER ENABLED GAME

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
               <key>com.apple.security.app-sandbox</key> <true/>
               <key>com.apple.security.network.client</key><true/>
               <key>com.apple.developer.game-center</key><true/>
</dict>
</plist>
 
 
You can find all of the supported .entitlements in Apple’s documentation. Also, once your .entitlements file is written, you don’t have to worry about modifying it again unless the features of your game changes. Just make sure you copy your entitlements file into your prep folder.
 
With .entitlements file added, your MacAppStorePrep folder (or whatever you chose to name it) should look something like this(using the name SuperGame as an example.)
/MacAppStorePrep
/SuperGame.app
/Distribution.provisionprofile
/SuperGame.entitlements
/optool
 
The final file we must generate is the one that will take all the information we’ve provided, process it, and assemble the end result we are seeking, a deliverable package compatible with the OSX app Store.
We are now making our .command file, hereafter referred to as our AppStorePrep.command file. As mentioned earlier, a OSX .command file is equivalent to a Windows .batch file. It is a series of commands that will be done by the OSX terminal which will validate, sign, and package our app. This .command file is replacing some of the automatic work done by the Unity OSX Editor and XCode.
For this, we’re going to take a look at a finished .command file and break it down line-by-line to explain what each step is doing. The example AppStorePrep.command file, with line numbers, is as follows:
 
  1. #!/bin/bash
  2. echo "Beginning App Store Prep for Nevermind"
  3. cd Desktop/MacAppStorePrep
  4. cp "Distribution_Profile.provisionprofile" "SuperGame.app/Contents/embedded.provisionprofile"
  5. PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:~/Desktop/MacAppStorePrep
  6. optool install -c weak -p /System/Library/Frameworks/GameKit.framework/Versions/A/GameKit -t SuperGame.app/Contents/MacOS/SuperGame
  7. codesign -f -v -s "3rd Party Mac Developer Application: Super Comany, LLC" "SuperGame.app/Contents/Frameworks/MonoEmbedRuntime/osx/libmono.0.dylib"
  8. codesign -f -v -s "3rd Party Mac Developer Application: Super Comany, LLC" "SuperGame.app/Contents/Frameworks/MonoEmbedRuntime/osx/libmonoPosixHelper.dylib"
  9. codesign -f -v -s "3rd Party Mac Developer Application: Super Comany, LLC" "SuperGame.app/Contents/Plugins/fmod.bundle/Contents/MacOS/fmod"
  10. codesign -f -v -s "3rd Party Mac Developer Application: Flying Mollusk, LLC" --entitlements "SuperGame.entitlements" "SuperGame.app"
  11. productbuild --component SuperGame.app /Applications --sign "3rd Party Mac Developer Installer: Super Company, LLC" SuperGame.pkg
  12. echo "App Store Prep Complete"
 
Now we can go about breaking down this file line by line.
 
  1. This line defines the .command file as a bash script. Fairly standard.
  2. Prints the words in quotation marks to the terminal window.
  3. Moves the script’s working directory so that everything it does is relative our prep folder. (In this case, the prep folder is on the desktop)
  4. We must copy the distribution provisional profile into the correct place within the SuperGame.app. This is something that XCode would handle automatically if we were building an app in Apple’s native toolset.
  5. Updates the OSX PATH variable temporarily to include our prep folder. This is to facilitate the use of the optool.
  6. We use the optool to ensure our app has proper linkage against OSX frameworks. In this example, the SuperGame uses GameCenter, thus it needs to link against Apple’s GameKit framework. This is the exact same thing the Unity Editor on OSX does if it detects your game is trying to use GameCenter in some capacity. The Window’s Editor can’t do this for us because the optool will only run on OSX.
  7. We can now begin signing our game using our 3rd Party Mac Developer Application certificate. Some subcomponents of our game will need to be signed otherwise they’ll be rejected by Apple’s automatic submission validator. If you try signing the top level .app before resigning things like unity's libmono.0.dylib, then you’ll end up invalidating the .app’s signature.
  8. The second of two subcomponents of our Unity app that needs to be signed.
  9. Once we’ve signed the two Unity dylibs, we can begin resigning our plugins. This is usually necessary because of the modifications we made on the Windows side to ensure unique bundle identifiers. When signing plugins, you normally just have to sign one file within the .bundle. Sometimes, however, you may find that you need to resign the whole plugin, including dylibs and the top level .bundle. If that occurs, remember that you must sign from the bottom up. You need to resign the files inside the .bundle before attempting to resign the .bundle itself.
  10. After signing the two necessary Unity files and any plugins within your project, you can now sign your top level app. This is also when you’ll apply the entitlements file you generated.
  11. The last step is to package your application into a .pkg file. Notice, unlike earlier codesign commands where we used the 3rd Party Mac Developer Application, for the productbuild command we need to use the 3rd Party Mac Developer Installer certificate. Also, check your OSX Keychain to get the exact name of your Application and Installer certificates since they need match exactly or the signing commands will not work.
With your .command file now written, your MacAppStorePrep folder should now have contents something like this:
 
/MacAppStorePrep
           /AppStorePrep.command
           /SuperGame.app
           /Distribution.provisionprofile
           /SuperGame.entitlements
           /optool
 
With all that done, you are literally only a few mouse clicks away from submitting your application to the OSX App store. From the OSX Finder, double click your AppStorePrep.command file. A terminal window will open, and if you did everything right, it will go about the process of automatically signing and packaging your build. When the process is complete, you should have a new .pkg file in your prep folder.
With the .pkg file ready, it’s time to begin to upload process. Open XCode’s Application Loader, select “Deliver Your App,” and then select the .pkg file from your prep folder. After that, just follow the Application Loader’s on-screen prompts and soon your app will be uploading to Apple’s servers.
After that, one of two things will happen. If your app fails the automatic validation, you or someone on your team should receive an e-mail detailing what issues the automatic validation detected that will need to be addressed. Generally, though, Apple’s errors are informative enough that you’ll be able to figure out what steps need to be taken to address the issue.
On the other hand, if your app passes Apple’s automatic validation process, then congratulations! You’re ready to submit your app for review. The more hands-on review process may expose a few more issues, but if not, then congratulations once more.  You have completed this guide and have successfully taken an OSX build made with Unity’s Windows Editor and performed all the necessary steps to have it be approved and released on the OSX App Store.
I hope this has been informative and helpful. Of course, this process could be modified further to your unique build pipeline. During the course of writing this article, I discovered there is a command line tool for Apple’s Application Loader. So, in theory, the .command script could also trigger the .pkg upload, making the whole process on the OSX side one-click once all the files are made and collected in the same place. Some automatization could also likely be done so that the App built by Unity’s Windows Editor doesn’t have to be manually copied to an OSX machine for processing.
Still, in the end, the process we described above is what worked for us. We hope that others who might be struggling with this issue can use this information to help get their application up on the OSX App Store. Finally, I hope this might inspire some other people to share their wisdom as well in case they’ve managed to crack a particularly difficult or interesting logistical problem when bringing a Unity-made game to the world.
This is Jesse Busch, Lead Engineer of Flying Mollusk, wishing you happy building.
ER
Erin Reynolds
3
Comments