Over the past couple of months I have been porting an existing Unity game to macOS. It is a shoot-em-up called Blue Rider developed by Ravegan from Córdoba, Argentina. It was originally released on PC (Steam), XBox, Playstation 4, and Switch.
I spent quite a lot of time getting it ported and running smoothly only to find that I couldn’t get past Apple’s notarization procedure because of the version of Unity I am using (5.6.7f1).
The binary uses an SDK older than the 10.9 SDK.
Notarization is required as of macOS 10.15 to distribute games in the Apple Store and it may become necessary on Steam. It may also be useful to notarize games for standalone distribution to avoid confusing warnings when users try to run your game.
(I am going to hold off a whole rant about Apple and their BS requirement to do this code signing/notarization at all. And I will try not to rant too much about their incredibly complicated process, documentation, and implementation.)
In this post I’ll describe the main problem I ran into and how I solved it. Hopefully it will help someone else.
Update – 27 December 2020
It looks like Unity fixed this in the last 2017 LTS version – 2017.4.40f1.
According to the release notes:
macOS: Fixed case where notarization would complain about mono being compiled with a older macOS SDk thus not allowing you to notarize your game.
So if you can, your best bet is to update to that version.
While the solution I propose allows a game to pass the notarization process, games based on Unity 5.6.7f1 (and, it seems, games based on Unity 2017.4 LTS) will not pass certification for the Apple Store.
When run on macOS 10.15, we now get this:
“Blue Rider” would like to receive keystrokes from any application.
Apparently this is because Unity uses an API that Apple no longer allows in the Apple Store, and Unity has not fixed either the 5.6 or the 2017 LTS version to replace it.
As far as I can tell right now, there is no way around this other than to update the Unity version but it is unclear what versions actually work.
So while the solution in this post doesn’t seem to help with Apple Store certification, it will let you notarize your Unity 5.6.7f1 or Unity 2017.4 LTS based game for Steam or for standalone distribution.
Once I worked my way through all the docs to figure out which certificates I needed for signing (one if you are submitting to the Apple Store, a different one if you are not!), what needed to be signed (the –deep option didn’t actually sign everything?), removing files that messed up signing (apparently *.meta files confuse it!), and then figuring out how to notarize on the command-line (‘xcrun altool‘?), I finally got to upload Blue Rider to Apple to try the notarization itself.
After running altool, waiting for an email to tell me the result (!), running yet another command on the command-line to get a link to the actual error log, then opening that in a browser (why not link directly in the email?), I found I had the following errors:
"statusSummary": "Archive contains critical validation errors",
"path": "Blue Rider.app/Contents/Frameworks/MonoEmbedRuntime/osx/libMonoPosixHelper.dylib",
"message": "The binary uses an SDK older than the 10.9 SDK.",
"path": "Blue Rider.app/Contents/Frameworks/MonoEmbedRuntime/osx/libmono.0.dylib",
"message": "The binary uses an SDK older than the 10.9 SDK.",
These two Mono libraries are added to the application by the Unity build process. The only “proper” way to update them is to update Unity. When you have a complete, tested game that’s almost ready-to-go, this is not a great option.
I poked around to see how other people handled this and it turns out Unity’s 2017 LTS (Long Term Support) version will not notarize because of this, and it’s possible the 2018 LTS does not work either. So many people using older versions of Unity are at the mercy of Unity’s support team to fix this problem.
Apple’s requirements are ridiculous and they don’t tend to respond to developers, and Unity likely won’t fix this—especially for older versions—so we developers are stuck in the middle.
Maybe we can hack it?
The first thing was to look at the dynamic library to see what’s up.
$ otool -l "Blue Rider.app/Contents/Frameworks/MonoEmbedRuntime/osx/libmono.0.dylib"
Among the output is the following:
Load command 8
So these libraries were compiled with a really old SDK, which is fine, but they also set the minimum SDK to 10.6 and this is what’s causing our problem.
Since Unity is closed-source, we cannot just recompile these libraries with an updated minimum SDK, but maybe we can change the library’s load command to a more recent SDK version?
I pulled out my trusty hex editor – Hex Fiend. The number “10.6.0” is stored as the integers “0 6 10” (backwards because of endianness), so I looked for 00060A00 in hex and found where it repeated (“version” and “sdk” in the output of otool above).
Then I edited the second one (the SDK version) to be 10.9 (00090A00):
After re-signing the application to account for these changes, and re-submitting for notarization, it was successful.
Another (Perhaps More Robust) Solution
After I’d got it working I looked around to see how else this might be done and I found a gist on GitHub that does exactly what we want. (Note 2 June 2020: The gist no longer exists – I have put up a repository with the code here.)
This little command-line tool mmaps the binary, finds the load command in the Mach-O header, and updates it to 10.9 if it is less than 10.9.
To use it, just download the code and compile it like this:
gcc fix_LC_VERSION_MIN_MACOSX.c -o fixMonoMinVersion
Then you can run it on the Mono dynamic libraries:
fixMonoMinVersion "<My Game>.app/Contents/Frameworks/MonoEmbedRuntime/osx/libmono.0.dylib"
fixMonoMinVersion "<My Game>.app/Contents/Frameworks/MonoEmbedRuntime/osx/libMonoPosixHelper.dylib"
This is great because I can add this process to the command-line scripts I use to build, sign, package, and notarize the game – no manual editing involved.
Does It Work?
So far, I have not found any issues during my testing of the game after these changes.
I cannot find any info online as to whether this is a “safe” thing to do or not. macOS does not include all previous SDKs, yet older apps run. So I believe that the SDK number here is more of a hint.
I would love to know more detail about how this works, so if you know anything about it, please comment below or contact me.
I assume no responsibility if these changes trash your car, burn down your house, or blow up your computer.
Use at your own risk and…
Test, test, test!
Slight Aside – LSMinimumSystemVersion
With macOS applications, it’s proper form to include a minimum version in its Info.plist file. Something like this:
With this, the application will refuse to run on any older version of the OS.
The version of Unity I’m using (5.6.7f1) does not include this, though I think later versions might.
If you are using an old version of Unity, you can use the following command-line to add it:
plutil -insert LSMinimumSystemVersion -string "10.10" "<My Game>.app/Contents/info.plist"
(Change “10.10” to whatever minimum you want to support.)
You will need to do this before you sign the application.
I have added this as part of my build scripts so it’s done automatically every time I do a build. (* Except that my build scripts no longer work when I try to update to Unity 2017.4 LTS.)
If you try this with your project, please let me know how it goes in the comments below or by contacting me.
If you found this post useful and want to support this project, please sign up for the Blue Rider mailing list and consider buying the game when it’s released!
Thank you! This is really super helpful. I can imagine how much time you required to spent on this investigation. This is very detailed, and thank you that you shared.
Thanks, this is super helpful! I was able to use this to get an old Wacom tablet driver from 2012 to notarize successfully! (I’m patching the driver to make it compatible with modern macOS).
Thanks Nicholas! Great to know that this post helped someone.