MacOS


Buckle up. This is going to be a bumpy ride. Don't blame me, this is all between Apple & Unity

So whats the problem ¯\_(ツ)_/¯?

Real talk: MacOS isn't a very popular platform for games, and Unity's support for it reflects that. At the time of this writing (April 2 - 2020) none of the stable versions of Unity will let you build a MacOS version as an XCode project.

Unity just builds a "vanilla" binary and calls it a day. This won't do. CloudKit requires certain entitlements to run, and when entitlements are involved, code-signing is required. OSX will terminate an unsigned app as soon as it tries to do something it is not entitled to do.

Without an XCode project to help with that, our only recourse is to build a MacOS version, crack it open, modify a bunch of files, and then resign all the binaries via the command line.

But don't worry, we created a script to make this as easy as possible.


Overview


At it's simplest, all you should need to do is configure your plugin's build settings and the PostProcess script takes care of resigning the MacOS application.

You will need to fill in:

  • Your code signing identity (which you get via KeyChain Access)
  • The path to your provisioning profile
  • Add your apps containers to the Custom Containers list
  • (optional) Provide a partial plist file to merge into the applications existing plist
  • (optional) Provide a partial entitlements file which gets merged into the applications existing entitlements file

Then just hit "build' and the post-process script takes care of the rest.

Simple right?

Well, hopefully. Truth is, code signing is never that easy. A lot can go wrong.

In this tutorial we'll provide a quick start guide. This is for people who are already familiar with the signing process. If your less familiar with the signing process then the step-by-step guide is the one for you. We'll guide you through double-checking your settings step-by-step and explain how the tools work in the process


Quick Start


You will need:

  • A Mac Certificate
  • A Provisioning Profile

Configure Build Settings

Open the Build Setting Asset inside the folder "Plugins->HovelHouse->CloudKit->Resources". In the inspector window: Make sure that "Post Process Mac Build" is checked. The following image shows the recommended settings for most users.

screenshot of the suggested build settings for MacOS


1. Find your Code Signing Identity

Open KeychainAccess and look for an entry of the form: Mac Developer: FirstName LastName (CERTIFICATE_ID). Select this certificate and copy this value to the clipboard.

Paste this value in the "Code Signing Identity" textfield in the plugins build settings.

2. Specify Your App's Containers

There is currently a bug with the MacOS build script that ignores the Add Default Containers checkbox in the settings. It is recommended that you disable this checkbox, and enter your apps default container manually in the Custom Containers list. The default container is of the form iCloud.{your-bundle-identifier}. If you don't fill in the correct value here, your app will fail codesigning.

3. Point to your Provisioning Profile

If you haven't already, make a developer provisioning profile for your app. Download it and place it somewhere.

In the inspector, hit the find button next to the text field and point it to your provisioning profile. Only files with the extension "*.provisionprofile" are selectable.

4. Create a custom plist (optional)

You can skip this step if your app does not require custom or additional plist entries.

Create a new plist file and save it somewhere on your computer

Open it in xcode and add your custom entries. You do not need to add the ones Unity adds by default

Use the find button to locate the file you created

5. Create a custom entitlements file (optional)

You can skip this step if your app does not require custom entitlements

The plugin will add the entitlements required by CloudKit, but if you have additional entitlements to add, the plugin provides a way for you to merge them into the build.

Create a new entitlements file

Add your custom entries

Use the find button to locate the file you created

6. Build

Known Issue: There is currently a bug where the code-signing script has its permissions modified by the unity package importer, which results in the script not being marked as executable. Building for MacOS may result in an error containing the following message: Native error= Access denied System.Diagnostics.Process.StartWithCreateProcess To fix this, you will need to chmod the file before it can be run from an editor process.

  • Locate the signing script in the unity project. It will be at the path /Packages/Cloud Kit Plugin/ShellScripts/Resign.sh. Right click it and select "show in finder" to open a window at it's installed location.
  • One up a terminal windown. Use the shortcut (cmd+space) to open the quick launch bar. Type "terminal" and hit enter.
  • Type: "chmod +x " then drag the signing script into the terminal window. The will copy the files path to the cursor position. Hit enter.
The next time you attempt to build for MacOS, you should no longer see this permissions error.

All you should have to do now is build. The post process build script will merge your plist and entitlements files if provided, then run a shell script which re-signs the application with the specified certificate and provisioning profile. Once signing is complete, you should be able to double-click your application to run it.



Step by Step


In this step by step guide we'll go through how the build script works and talk about some of the common pitfalls in the code signing process. This is useful information for Troubleshooting your MacOS build when code-signing fails.

Configure Build Settings

Open the Build Setting Asset inside the folder "Plugins->HovelHouse->CloudKit->Resources".

In the inspector window: Make sure that "Post Process Mac Build" is checked.

1. Find your Code Signing Identity

If you haven't already done so, you will need to create a certificate for you MacOS app on the apple developer portal and install it on your computer. This certificate will need to be present in the provisioning profile you sign with. Confused? Don't worry we'll cover that later.

Once the certificate is installed, we need to find it's Common Name and paste it into the textarea labeled Code Signing Identity

Open KeychainAccess and look for an entry of the form Mac Developer: FirstName LastName (CERTIFICATE_ID). Select this certificate and copy this value to the clipboard. Pay close attention to the spelling. You may have another entry that starts Apple Developer: FirstName LastName... don't select that one. It won't work.

Paste this value in the Code Signing Identity textfield in the plugins build settings.

This is a critical step. If you select the wrong identity here, the codesigning script has no way of knowing. The script will sign the app as if everything is working fine, but your app will crash on launch with an "Invalid Signature" exception
This value is passed as a parameter to CodeSign command when the post process build script runs. It is used to identify the certificate we want to sign with. Many other values will work for this field, including your certificates SHA-1 hash. See the paragraph labeled "Signing Identites" on the man page for the codesign command to learn about what other options work here

2. Specify Your App's Containers

There is currently a bug with the MacOS build script that ignores the Add Default Containers checkbox in the settings. It is recommended that you disable this checkbox, and enter your apps default container manually in the Custom Containers list. The default container is of the form iCloud.{your-bundle-identifier}. If you don't fill in the correct value here, your app will fail codesigning.

3. Locate your provisioning profile

If you haven't already done so. You'll need to create a provisioning profile for your MacOs application. This is typically the most error prone part of the signing process, as it is easy to misconfigure.

Download it and use the find button to locate it on your hard drive. This path is passed as a parameter to the code signing script

A provisioning profile has a list of things that all need to be correct in order for your app to run. If any one of them is incorrect, codesigning will complete as if nothing bad has happened, but your app will crash with a codesigning error on startup. The error message Apple provides contains no information as to which of these things is misconfigured. Your only recourse is to double check each of these things one by one.

They are as follows:
  • A list of certificates that can use this profile (the one you created must be in this list)
  • The bundle id of the app it's for (must match the bundle id of the app you sign)
  • A list of devices that your app will run on (your app wont run on your computer unless it's listed)
  • A list of app capabilies or entitlements (must match the capabilities your app was built with)

You can typically verify this information by right clicking on the provisioning profile, and selecting get info. The preview view will show you a list of key-value pairs that you can use to verify that you have set everything up correctly.

The provisioning profile needs to contain the certificate you created for the previous step. The app-id in the profile also needs to match the app-id in unity's build settings. If either is missing, code signing will fail.
Changing the capabilies your app uses will invalidate your provisioning profile, and cause code signing to fail. You will need to login to the apple developer portal to regenerate it, download it again, and reinstall it. XCode projects can do this automatically, but you will have to do this manually for your MacOS application.

4. Create a custom plist (optional)

Codesigning is used (among other things) to verify that your app has not been tampered with. This means that the plist file cannot be changed after codesigning has completed. If your app requires custom plist entries, the plugin provies a way for you to add them to the mac build before the codesigning script is run.

The plugin itself doesn't require any modifications to the plist to run, so if you do not have edits to make, you can skip this step by leaving the merge plist step unchecked

The plist file you make will be merged into the default one provided by unity, so you do not need to provide a complete file. Just create one with the entries that are missing, this'll ensure that the entries that Unity modifies (like bundle version) don't get clobbered

Place your partial plist somewhere in your project, and use the find button to point to it.

5. Create a custom entitlements file (optional)

Similar to the previous step, an apps entitlements are using during codesigning and cannot be changed after codesigning occurs. The plugin provies a way to merge in any additional entitlements into the ones required by CloudKit before code signing occurs.

If your app does not have additional entitlements to add, you can skip this step by unchecking the Perform Entitlements Merge option.

The merged entitlements file is saved to the build directory, the path to this file is then sent as an argument to the build signing script

6. Build

Known Issue: There is currently a bug where the code-signing script has its permissions modified by the unity package importer, which results in the script not being marked as executable. Building for MacOS may result in an error containing the following message: Native error= Access denied System.Diagnostics.Process.StartWithCreateProcess To fix this, you will need to chmod the file before it can be run from an editor process.

  • Locate the signing script in the unity project. It will be at the path /Packages/Cloud Kit Plugin/ShellScripts/Resign.sh. Right click it and select "show in finder" to open a window at it's installed location.
  • One up a terminal windown. Use the shortcut (cmd+space) to open the quick launch bar. Type "terminal" and hit enter.
  • Type: "chmod +x " then drag the signing script into the terminal window. The will copy the files path to the cursor position. Hit enter.
The next time you attempt to build for MacOS, you should no longer see this permissions error.

Unity will make a MacOS binary. The plugin will then modify the plist file, create an entitlements file, and then run a codesigning script located in "Packages/CloudKit/ShellScripts/resign.sh" with the arguments you provided in the inspector. If there are any errors, they will show in the console window.


Troubleshooting

(╯°□°)╯︵ ┻━┻


The PostProcess Build step conflicts with another

If you have your own custom build script, or another native plugin installed, there's a chance it could conflict with this one. We make every effort to insure that our script plays friendly with others, but it's not possible to cover every use case.

To make your life easier, each step of the post process script can be disabled. Additionally, the bash script which does the signing may be run by itself in terminal with arguments you provide

The app crashed on startup

This is usually an indicator that the app was signed with a misconfigured provisioning profile. There can be many reasons this can happen, and sadly, the crash report will never say exactly what the problem is. The only thing we can do is double-triple check that our provisioning profile is set up correctly.

The app crashed after the splash screen

If your app is FairPlay encrypted, OSX will not permit it to run outside of the applications folder. Drag your build app into the applications folder and run it from there

The app crashed after my first call to a CloudKit function

This is usually an indicator that the app wasn't signed with the correct entitlements.