Impact Of New Notarization Process For Osx Apps?

I've just been reading the new documentation on Notarizing Your App (for OSX) which is going to be changing. Will this require us to go through a two-step build process for OSX, and what version of Unity will be necessary to work with this?

https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution?language=objc

I'm also looking into notarizing one of our MacOS apps. Unfortunately there's isn't much information about notarizing a Unity-based app.

Anyone who already managed to do this successfully?

I got as far as uploading it successfully. I can't see a way past the errors "The binary uses an SDK older than the 10.9 SDK" and "The executable does not have the hardened runtime enabled"

I think until Unity fix it we're stuck.

1 Like

I can't speak specifically to Unity 2019, but for a Unity 2018.4 Mac OS build to pass Apple's notarization process I had to open the .NIB files created by Unity in Xcode and change the "Opens In" and "Builds for" to the desired Xcode version and target macOS release. After doing this the build could pass the Apple Notarization process. Please note we aren't distributing in the Mac App Store so there might be other steps required that we didn't need to perform, we just wanted the scary warnings to go away when customers launched our installer.

There are many other pitfalls along the path to passing Apple's notarization process, but this one is likely to affect anyone trying to distribute Mac OS standalone apps.
Here are a couple tips to help others:
- All platform native precompiled libraries need to live in the Contents/Frameworks/ subfolder within your app bundle.
- Even though Apple's documentation says it is possible and recommended to notarize a .dmg file containing an installer, this actually fails and does not work. Skip notarizing the DMG.
Edit: more info on this:
https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/

I'd love to hear others experiences with this and any advice they would offer.
Best of luck!

1 Like

Many thanks, I believe its fixed now in 2019.2 so is okay.

Is there an update to what the process is on the new unity builds? Steam is starting to require that all OSX games be notarized soon, as well.

Yes, I was wondering how this will affect MacOS users for my game. Will you need to own a Mac in order to build games for it now?

1 Like

Unity's documentation has info about prepping your build for the mac app store, but that is NOT the same thing. That's more involved and involves you paying apple money. It's probably worth it, but it's a small enough market I haven't bothered. My games were in the very first wave of OSX games on Steam, and I've always been pleased with that (it's thanks to Unity), but I'd hate to see that all being undermined now 8 years later.

I know you need an Apple Developer ID to notarize, but do you also need to pay to be a registered developer to notarize also? It does seem like you can notarize with the command line, so it should be pretty scriptable... although you'll still need a Mac with xcode installed.

I found it pretty straightforward with 2018.4.4 through 2018.4.9. I have a post-process ant script that will codesign the app bundle generated from Unity and then submits it for notarization.

Here's a general-purpose version of my ant script (under MIT License), in case anyone might find it useful. Due to keychain restrictions, it has to be run locally on the Mac - codesign can't properly access the keychain when running from a remote session:

<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright © 2019 Hidden Achievement LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<project name="Unity-Mac-Notarize" xmlns:if="ant:if" xmlns:unless="ant:unless" default="notarize" basedir=".">
    <taskdef resource="net/sf/antcontrib/antlib.xml" />
    <description>
        Signs and notarizes a Unity (or other) app on macOS
    </description>

    <!--
        BASIC SETUP

        This script assumes that you place some support files in the defined 'support' folder
         By default, the support folder is "etc" under the directory where this script is placed.
         That folder should contain:
         Info.plist
         <app.bundle_id>.entitlements
         apple/<language>.lproj

        in the Info.plist, some variables will be replaced:
        ${version} replaced with the value of build.number
        ${shortVersion} replaced with the value of build.version
        ${iconname} replaced with the app executable base name

        When calling the script, the following properties are required, either on the command-line or in local.properties:
          app.executable_name - the base name of the executable (e.g. for MyGame.app, this would be MyGame)
          app.bundle_id - the bundle ID of the app (e.g. com.MyCompany.MyGame)
          codesign.key  - the SHA-1 signature of the codesigning key
          notarize.account - the account used for notarization (generally the email address for the Apple ID)
          notarize.keychain_key - the keychain key containing the app-specific password for notarization
          build.version - the version string for the build (usually a 3-part version like 1.2.3)
          build.number - the build number of the build (an integer)

        Optionally, the following options can also be provided. Defaults are in square-brackets.
          support_folder [./etc] - folder containing support files, relative to location with this script
          output.unsigned [./Builds/Unsigned] - folder containing the unsigned Unity output .app
          output.signed [./Builds/Signed] - folder that will contain the signed output
    -->


    <!-- load in local machine settings -->
    <property file="./local.properties" />

    <!-- Folder containing support files -->
    <property name="support_folder" location="./etc"/>

    <!-- Properties to pass in to the ant task - or modify the script and define here -->
    <!-- base name of the app bundle (e.g. for MyGame.app, this would be MyGame) -->
    <!-- <property name="app.executable_name" value="MyGame"/> -->
    <fail unless="app.executable_name"/>
    <!-- app bundle id -->
    <!-- <property name="app.bundle_id" value="com.mycompany.mygame"/> -->
    <fail unless="app.bundle_id"/>

    <property name="output_root" location="./Builds/" />

    <!-- Folder containing the unsigned build from Unity -->
    <property name="output.unsigned" location="${output_root}/Unsigned"/>
    <property name="output.signed" location="${output_root}/Signed"/>

    <property name="signed_bundle" location="${output.signed}/${app.executable_name}.app"/>

    <!-- These must be set, preferably in local.properties -->
    <fail unless="codesign.key"/>
    <fail unless="notarize.account"/>
    <fail unless="notarize.keychain_key"/>

    <!-- optional arguments -->
    <!-- codesign.keychain -->
    <!-- <property name="codesign.keychain" value="login"/> -->

    <!-- required -->
    <fail unless="build.version" message="Missing required build.version"/>
    <fail unless="build.number" message="Missing required build.number"/>

    <target name="codesign">
        <echo message="--- Signing macOS Build in ${output.unsigned} for notarization to ${output.signed} ---"/>
        <mkdir dir="${output.signed}"/>
        <!-- Copy the unsigned bundle to the signed target location -->
        <copy todir="${signed_bundle}" overwrite="true">
            <fileset dir="${output.unsigned}/${app.executable_name}.app">
                <exclude name="**/*.meta"/>
            </fileset>
        </copy>

       <!-- Replace and update Info.plist - set the version numbers and icon -->
        <local name="version"/>
        <property name="version" value="build.number"/>
        <local name="shortVersion"/>
        <property name="shortVersion" value="build.version"/>
        <local name="iconname"/>
        <property name="iconname" value="${app.executable_name}"/>

        <copy file="${support_folder}/Info.plist" tofile="${signed_bundle}/Contents/Info.plist" overwrite="true">
            <filterchain>
                <expandproperties />
            </filterchain>
        </copy>
        <copy todir="${signed_bundle}/Contents/Resources" overwrite="true">
            <fileset dir="${support_folder}/apple">
                <include name="*.lproj/**"/>
            </fileset>
        </copy>

        <!-- sign internal bundles -->
        <foreach target="codesign_helper" param="signFile">
            <path>
                <fileset dir="${signed_bundle}/Contents/Plugins" casesensitive="yes">
                    <type type="dir"/>
                    <include name="**/*.bundle"/>
                </fileset>
            </path>
        </foreach>
        <!-- sign dylib files -->
        <foreach target="codesign_helper" param="signFile">
            <path>
                <fileset dir="${signed_bundle}/Contents/Frameworks" casesensitive="yes">
                    <include name="**/*.dylib"/>
                </fileset>
            </path>
        </foreach>

        <!-- codesign the app -->
        <exec executable="/usr/bin/codesign" failonerror="true">
            <arg value="--force"/>
            <arg value="--deep"/>
            <arg value="--verify"/>
            <arg value="--verbose"/>
            <arg value="--timestamp"/>
            <arg value="--options"/>
            <arg value="runtime"/>
            <arg value="--keychain" if:set="codesign.keychain"/>
            <arg value="${codesign.keychain}" if:set="codesign.keychain"/>
            <arg value="-s"/>
            <arg value="${codesign.key}"/>
            <arg value="--entitlements"/>
          <arg value="${support_folder}/${app.bundle_id}.entitlements"/>
            <arg value="${signed_bundle}"/>
        </exec>

        <exec executable="/usr/bin/codesign" failonerror="true">
            <arg value="--verify"/>
            <arg value="--verbose=4"/>
            <arg value="--keychain" if:set="codesign.keychain"/>
            <arg value="${codesign.keychain}" if:set="codesign.keychain"/>
            <arg value="${signed_bundle}"/>
        </exec>
    </target>

    <target name="notarize_macos" unless:set="skip-notarize" depends="sign_macos">
        <!-- zip for notarization -->
        <zip destfile="${output_dir}/${app.executable_name}.app.zip">
            <fileset dir="${output.signed}">
                <include name="${app.executable_name}.app/**"/>
            </fileset>
        </zip>

        <exec executable="/usr/bin/xcrun" failonerror="true">
            <arg value="altool"/>
            <arg value="--notarize-app"/>
            <arg value="--type"/>
            <arg value="osx"/>
            <arg value="--primary-bundle-id"/>
            <arg value="${app.bundle_id}.zip"/>
            <arg value="--username"/>
            <arg value="${notarize.account}"/>
            <arg value="--password"/>
            <arg value="@keychain:${notarize.keychain_key}"/>
            <arg value="--file"/>
            <arg value="${output_dir}/macOS/${app.executable_name}.app.zip"/>
        </exec>
    </target>

    <target name="codesign_helper">
        <echo message="Code sign ${signFile}"/>
        <exec executable="/usr/bin/codesign" failonerror="true">
            <arg value="--force"/>
            <arg value="--deep"/>
            <arg value="--verify"/>
            <arg value="--verbose"/>
            <arg value="--keychain" if:set="codesign.keychain"/>
            <arg value="${codesign.keychain}" if:set="codesign.keychain"/>
            <arg value="-s"/>
            <arg value="${codesign.key}"/>
            <arg value="${signFile}"/>
        </exec>
    </target>

    <target name="notarize" depends="codesign,_notarize">
    </target>

    <!-- Staple the received notarization to the app -->
    <!-- run this after notarization is completed -->
    <target name="staple">
        <echo message="--- Stapling notarization to Mac Build at ${signed_bundle} ---"/>
        <exec executable="/usr/bin/xcrun" failonerror="true">
            <arg value="stapler"/>
            <arg value="staple"/>
          <arg value="${signed_bundle}"/>
        </exec>
    </target>
</project>
2 Likes

See this KB article: https://support.steampowered.com/kb_article.php?ref=1055-ISJM-8568

I do frequent patches (sometimes a few a week), and I can't imagine having to cite up my Mac every time I run a build. That would add so much extra time to the whole process. I've got everything scripted out on windows to build for all three OSes, and I only run my Mac when I need to test something specific on it.

I'm assuming that unity is not expecting us to all start building on a Mac for osx builds, although I'm sure Apple would love that. If this is the route that Apple takes, then unless there's something more efficient for pushing to osx I might wind up dropping osx support above 10.14 and just support windows and Linux. What a bizarre situation to be in.

Notarizing every app build is cost preventative as a one person team. Because of this and various issues like often having to rewrite sharers to work on mac I'll likely be dropping OSX support.

1 Like

What sort of shaders are you having to rewrite? Are you doing a lot with tessellation or something? Other than that, I think most things are feature-parity across those platforms.

But yes, notarizing every app build is going to be bonkers for a one person team like me, too. This is not the sort of future I ever envisioned for the Apple platform: where the code all finally works there, and has done for most of a decade, but they’re chasing out developers in an effort to improve security (which hasn’t seemed to really be a big issue on that platform anyway).

It’s just nuts; I thought they wanted games on their platform.

That’s one motive that one could ascribe to them. I was thinking this is a step in the direction of turning MacOS into a closed platform, like iOS already is. It also makes it harder to develop for MacOS without actually using a Mac…

On iOS, Apple has a lot more control over what types of apps can run on a consumer’s device. If an app doesn’t match Apple’s vision for their platform, they can reject it. That might be an app with a security risk, objectionable content, or competing with Apple’s other products. Apple isn’t reviewing OS X apps now, but now they have the technical means to un-notarize an app and prevent it’s execution. I think they’re slowly moving towards the Mac App Store being the only way to install software. That would also guarantee Apple a cut of all software used on their platform.

@x4000
Regarding shaders: We have y-flipping issues on basically every one of our shaders that runs on OpenGL and DirectX … because no one ever remembers the UNITY_UV_STARTS_AT_TOP ifdef. There’s actually a pretty big list of things to remember.
https://docs.unity3d.com/Manual/SL-PlatformDifferences.html

We’re sort of approaching the point where you’re likely to forget about OpenGL in favor of metal on mac… But you’ll definitely have users with hosed games at this point if you forget to test on all 3 graphics APIs. Linux is probably going to default to OpenGLCore.

From here it says:

You can enroll in the Apple Developer Program here - it costs $99/year as usual (see here):

See also:

2 Likes

If I have to join the apple developer program, run xcode on every build, and so on, I think I'm done with osx. I released 5 builds last week; thanks to Steam, my total time from compile scripts finishing to being live on three OSes was about 30 minutes, and the bulk of that time was spent writing some added stuff for the release notes.

Just transferring the software to my mac to then upload it (let alone get a result) would tack on 20+ minutes per build. So I'm guessing an added hour or so added total with that, per release, after I start paying apple yearly as well as get a new build script process up and running.

To me this is absolutely a pitchfork-style situation. It definitely sounds like they want a closed OS like iOS, and I don't support that in the slightest. Hell, I was excited to give people OSX versions of my games partly specifically so they wouldn't be locked into Windows. Now there's Linux, which is even better for freedom on that front.

On my most recent product, literally 2% of the customers have booted it up on a mac, and 2% on linux. I'm sure there's some substantial overlap between those groups, but Steam doesn't show those particular stats.

On my most popular product, I've had 703,263 copies total on Steam, 15,185 of which were ever associated with a mac, and 4,047 of which were ever associated with a linux box. That's... again 2% for OSX, almost exactly. Less for linux since that game is 10 years old and linux wasn't supported until Steam and Unity both added that capability.

My second most popular product has just under 4% of people having loaded it on a mac at some point, and that's the highest number I have out of any of my games. That one came out in 2014, and the linux install rate is only 1.5%, so that's interesting.

There's just no way I can justify the costs -- time costs in particular -- to jump through these hoops apple is setting out.

2 Likes

I've just discovered this issue myself, as I'm going through the submission process for a new game on Steam, and I just wanted to echo your feelings @x4000 .

As a solo indie developer, it's just not worth it for me to pay that annual fee and devote all that extra time and energy to making Mac builds for every patch, when they've historically only represented less than 5% of my total revenue.

1 Like

@HiddenJason Do we need to notarise the app before processing for steam or after content prepping for the mac file?