How to publish Electron application to Mac App Store

In this article I'll try to provide a step-by-step guide describing publishing of MacOS Electron.JS application to Mac App Store highlighting all tricky moments.

First of all, everything said here is relevant to the early 2020.

The most important thing: only applications based on Electron 5.0.13 and 6.1.7 can be published due to Apple policy. Probably something will be changed soon.

Important definitions

Sandboxing - isolation of your application in special environment with restricted access almost to everything. See original apple video explaining this.

Entitlements - request for permissions for your sandboxed application. Full security entitlements list can be found here.

Provisioning profile - it's something that connect your developer account (certificate), bundle and devices that can install your application.

Publication

Let's imagine that you already have Electron application and you are using Electron builder to build your application.

All listed code can be found at our Electron Nuxt repository..

1. Go to Certificates, Identifiers & Profiles section at developers.apple.com. At Identifiers section Add new App ID for macOS platform.

Next go to Profiles and create new Provisioning profile for Mac App Store distribution.

Download created profile and place to build folder inside your application source folder. Name it as embedded.provisionprofile

2. Go to App Store Connect and select My Apps. Add New macOS App.

Select registered on the previous step Bundle ID.

Provide all required information.

3. Inside build folder create entitlements.mas.plist with at least the following entitlements:

 
                     <!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.cs.allow-unsigned-executable-memory</key>
                        <true/>
                        <key>com.apple.security.cs.allow-jit</key>
                        <true/>
                        <key>com.apple.security.application-groups</key>
                        <array>
                            <string>DEVELOPERID.full.package.name</string>
                        </array>
                    </dict>
                </plist>
        
        

To find your DEVELOPERID go to Membership tab ad developers.apple.com. You can find it under Team Name as Team ID. full.package.name is your registered App Identifier.

At the “build” folder create entitlements.mas.inherit.plist with

 
              <!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.inherit</key>
                <true/>
                <key>com.apple.security.cs.allow-jit</key>
                <true/>
                <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
                <true/>
            </dict>
        </plist>
        
        

And entitlements.mas.loginhelper.plist with

 
          <!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>
        
        

4. Go to package.json file and add to the build section.

 
        
"mac": {
    "target": [
        "mas"
      ]
},
   "mas": {
      "hardenedRuntime": false,
      "provisioningProfile": "build/embedded.provisionprofile",
      "entitlements": "build/entitlements.mas.plist",
      "entitlementsInherit": "build/entitlements.mas.inherit.plist",
      "gatekeeperAssess": false,
      "asarUnpack": []
    }
        
        

5. At this point Electron-builder configuration is finished and you can run electron-builder command to package your app. In case you have multiple developers certificates you need to provide appropriate certificate name as

 CSC_NAME=\"CERTIFICATE NAME (***********)\" electron-builder        

6. After build process resulting file can be found at dist/mas/*.pkg and should be uploaded to Mac App Store via Transporter application.

Unfortunately, in some cases this is not enough and application is getting rejected as not all binaries are signed during packaging by Electron builder. To fix this after step 4 you should:

7. Create build/resignAndPackage.sh script

 
        
            
#!/bin/bash
# Name of your app.
APP="APPLICATION NAME"
# The path of your app to sign.
APP_PATH="dist/mas/$APP_PATH.app"
# The path to the location you want to put the signed package.
RESULT_PATH="dist/mas/$APP-mac_store.pkg"
# The name of certificates you requested.
APP_KEY="3rd Party Mac Developer Application: CERTIFICATE NAME (***********)"
INSTALLER_KEY="3rd Party Mac Developer Installer: CERTIFICATE NAME (***********)"
# The path of your plist files.
PARENT_PLIST="build/entitlements.mas.plist"
CHILD_PLIST="build/entitlements.mas.inherit.plist"
LOGINHELPER_PLIST="build/entitlements.mas.loginhelper.plist"
FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Libraries/libffmpeg.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/Contents/MacOS/$APP Helper"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper.app/"
codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/Contents/MacOS/$APP Login Helper"
codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacOS/$APP"
codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH"
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH"
        
        

In build/resignAndPackage.sh change APPLICATION NAME to your application name (should be as productName in package.json). Replace CERTIFICATE NAME (***********) to your certificate name in two places.

8. Binaries that are listed in build/resignAndPackage.sh valid for Electron 5.0.13. You need to updated them for another Electron version or in case additional binaries will be used. To find binaries run (source)

 find -H YourAppBundle -print0 | xargs -0 file | grep "Mach-O .*executable"        

Binaries from node_modules can be unpacked by listing them in asarUnpack key in package.json

9. Run

        CSC_NAME=\"CERTIFICATE NAME (***********)\" electron-builder && bash build/resignAndPackage.sh

and upload dist/mas/$APP-mac_store.pkg to Mac App Store via Transporter application.

That's all. Probably you'll have "an amusing trip" with Apple team explaining them why you've built your application, why you need permissions listed at entitlement files, but it's another story :-)

Building something amazing with Electron? Contact us at webspaceteam.com - we always ready to help you with your project.

Let’s get in touch

Describe your project or need, send requirements or whatever you need. We will get in touch with you in 1 business day.