Introduction

You have two options for distributing an Electron App for MacOS. Either directly (see Notarizing Your Electron Application), or through the Mac App Store (MAS). All apps distributed though the MAS must be sandboxed, meaning they are completely self contained except for any approved entitlements. Below are the steps you need to take to get a basic app successfully submitted for review and approved for distribution on the Mac App Store.

You will need to package two versions of your app. A development version for testing, and a distribution version for submission to Apple.

Reference: developer.apple.com/app-store/review
developer.apple.com/app-store/review/guidelines
electronjs.org/docs/tutorial/mac-app-store-submission-guide


1) Starting with your completed Electron Application.

You have to use Electron version 8.0.2 or later (or patched versions 6.1.7 and 5.0.13) since earlier versions used private APIs which Apple no longer allows.

Electron should be a development (or global) dependency not a production dependency.

  • npm install -D electron

  • 2) Apple Developer Account

    You need an Apple Developer Account. Costs ~$100/year. Sign up at developer.apple.com.


    3) Create Three Signing Certificates:

    Using XCode

    Create and install these three certificates with XCode. Repeat the below process for each certificate.

    Open XCode > Click the XCode menu > Select Preferences > Click Accounts > Click the Manage Certificates button > Click the + button in the lower left corner > Select the certificate type to create (Development, Distribution, Mac Installer Distribution).

    Using the Keychain Access app

    Alternatively, you can create and install these three certificates with the Keychain Access app. Repeat the below two-step process for each certificate.

    Step 1) Request a certificate:
  • Launch Keychain Access located in /Applications/Utilities.
  • Click the Keychain Access menu > Certificate Assistant > Request a Certificate from a Certificate Authority.
  • In the Certificate Assistant dialog, enter an email address in the User Email Address field.
  • In the Common Name field use the value provided.
  • Leave the CA Email Address field empty.
  • Choose "Saved to disk" saving it to the desktop, and click Continue. Filename is CertificateSigningRequest.certSigningRequest

  • Step 2) Import the new certificate from your developer account to your Keychain:
  • go to https://developer.apple.com/account > Certificates > + (to add a new certificate) > Select the certificate type (Apple Development, Apple Distribution, Mac Installer Distribution) > click the continue button > Choose File > Double-click the certificate file you downloaded above > Download the new certificate file > Double click the downloaded file and it will be automatically loaded to your keychain.
  • Delete the CertificateSigningRequest.certSigningRequest and the certificate download files from the desktop.

  • To view the certificates:

  • To view the certificates in XCode: Open XCode > Click the XCode menu > Select Preferences > Click Accounts > Click the Manage Certificates button > the signing certificates will be displayed.
  • To view the certificates in your keychain: Open the Keychain Access app in the Application Utilities folder > Click on the Login Keychain (should be selected by default) > Select Category: MyCertificates > they should be listed there along with your Team Name and ID number. For a solo developer your team name is just your name.
  • To view the certificates in you developer account go to developer.apple.com > Certificates, IDs, & Profiles menu > Certificates menu > Development, Distribution and Mac Installer Distribution type certificates should be listed there.

  • 4) Create an App ID

  • Go to your developer account - developer.apple.com > Certificates, IDs & Profiles > Identifiers > + (to add a new id) > select App IDs > continue button.
  • On the Register App ID page:

  • 5) Add the App to your developer account

    Reference: help.apple.com/app-store-connect
    Official instructions for adding an app: help.apple.com/app-store-connect/#/dev2cd126805
  • Go to appstore connect
  • Click on My Apps > + (to create a new app) > select New MacOS App > Select the appId you registered previously.
  • Fill out the App Info section:
  • Fill out the Pricing and Availability section:
  • Fill out the MacOS section:

  • 6) Create an icon set

    Reference: electron.build/icons | developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/app-icon
  • You need to create your icon in at least two different sizes: 512x512 px and 1024x1024 px saved as png files. But ideally use all the Apple recommended sizes which additionally include 16x16, 32x32, 64x64, 128x128 and 256x256.
  • Follow this guide: How to create high resolution icns files. A few things to highlight:

  • 7) package.json file

    7a) Development version (for testing)

    The values in italics need to be changed to your info.

    { 
      "name": "AppName",  
      "version": "1.0.0", 
      ...
      "author": "AuthorName", 
      "build": {  
        "appId": "com.companyname.appname",
        "productName": "App Name (can include spaces & special characters)", 
        "buildVersion": "1.0.0",
        "copyright": "Copyright © 2022 Developer or Company Name",
        "mac": {  
          "category": "public.app-category.categoryName", 
          "icon": "build/icon.icns",  
          "target": "mas-dev",  
          "hardenedRuntime": false, 
          "entitlements": "build/entitlements.mas.plist", 
          "entitlementsInherit": "build/entitlements.mas.inherit.plist",  
          "provisioningProfile": "build/AppleDevelopment.provisionprofile",
          "type": "development"
        } 
      }
      "scripts": {
        "postinstall": "electron-builder install-app-deps",
        ...
      },
      ...
    }
    

    7b) Distribution version (for submission to the Mac App Store)

    The values in italics need to be changed to your info.

    { 
      "name": "AppName",  
      "version": "1.0.0", 
      ...
      "author": "AuthorName", 
      "build": {  
        "appId": "com.companyname.appname",
        "productName": "App Name (can include spaces & special characters)", 
        "buildVersion": "1.0.0",
        "copyright": "Copyright © 2022 Developer or Company Name",
        "mac": {  
          "category": "public.app-category.categoryName", 
          "icon": "build/icon.icns",  
          "target": "mas",  
          "hardenedRuntime": false, 
          "entitlements": "build/entitlements.mas.plist", 
          "entitlementsInherit": "build/entitlements.mas.inherit.plist",  
          "provisioningProfile": "build/MacAppStore.provisionprofile"
        } 
      }
      "scripts": {
        "postinstall": "electron-builder install-app-deps",
        ...
      },
      ...
    }
    

    Only include the postinstall script above if you have native dependencies that need to be recompiled by Electron (see section 13 below).

    Reference:
  • General configuration including metadata: electron.build/configuration/configuration
  • Mac-specific configuration: electron.build/configuration/mac
  • MAS-specific configuration: electron.build/configuration/mas

  • A few things to note:
  • Name and productName: The name key will be used as the display name for your app on the user's computer. It cannot contain spaces or special characters. If you want to use spaces or special characters in the name then add a productName key within the build key. You set the name of your app as it appears in the Mac App Store at appstoreconnect.apple.com.
  • Version and build number: The version number in package.json should correspond with the version number of your app at appstoreconnect.apple.com. If you upload your app to appstoreconnect but decide to make changes before submitting it for review, you can't delete what you uploaded. Rather you submit a revised app with a different build number. Electron-builder sets the build number equal to the version number in the package.json file which is problematic if you need to submit a second build. To set a different build number, use the buildVersion property.
  • Use the same appId as you created for your app at developer.apple.com.
  • The category key should align with the category you set for your app in appstoreconnect.apple.com. Mac uses this key in the Finder via View > Arrange by Application Category when viewing the Applications directory. List of possible categories.
  • Set your target to "mas-dev" if building the development version (for testing) and "mas" if building the distribution version (to submit to the MAS).
  • Set Hardened Runtime to false. You would set it to true if you want to distribute the app outside the MAS. For more on hardened runtime see developer.apple.com/documentation/security/hardened_runtime_entitlements
  • Set type to "development" for the development version.

  • Keys you don't need to set because the default is already correct:
  • The icon key points to where your icon.icns file is. The default folder and filename is build/icon.icns so you can leave it out if it is located there.
  • The type key can be distribution or development. Distribution is the default so you can leave this key out.
  • The identity key sets the name of the certificate to use when signing. However, for the MAS build, the signing certificate will be taken from the provisioning profile (covered below). And Electron-builder will automatically use the 3rd Party Mac Developer Installer certificate in your Keychain to sign the pkg file.
  • GatekeeperAssess key is set to false by default. Mac's Gatekeeper ensures that apps distributed outside the app store are signed and notarized. Since this build is for the MAS you can leave this at false.

  • 8) Entitlements

    Reference: electronjs.org/docs/tutorial/mac-app-store-submission-guide
  • Make parent and child entitlement property list files using XML. Put them in the build folder.
  • These files correspond to the entitlements and entitlementsInherit keys in the package.json file.
  • The parent entitlement file:

    /build/entitlements.mas.plist
    <?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.application-groups</key>  
        <array> 
          <string>TEAM_ID.com.companyname.appname</string>  
        </array>  
        <!-- Put any entitlements your app requires here. Below is an example -->  
        <key>com.apple.security.files.user-selected.read-write</key><true/> 
      </dict> 
    </plist>  
    

    Child entitlement file (inherits from the parent):

    /build/entitlements.mas.inherit.plist
    <?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.inherit</key><true/>  
      </dict> 
    </plist>  
    
  • First thing to notice is app-sandbox is set to true. All apps in the MAS must be sandboxed. That means they don't have access to resources outside the app, unless you specifically request an exception (an entitlement) and it is approved. For example to be able to read and write to files in the user's computer (outside the app) you need to request user-selected files read-write access. If approved your app can read/write files that users have specifically opened or saved using the mac Open or Save dialog box.
  • These entitlements must correspond to the entitlements you request for your app in Appstoreconnect.apple.com
  • Read about entitlements here.
  • The list of available app sandbox entitlement keys are here.
  • The application-groups key is an array containing your app identifier (TeamID and appId).

  • 9) Generate two Provisioning Profiles (AppleDevelopment and MacAppStore)

    Repeat the below steps for Apple Development and Mac App Store

  • Go to your apple developer account profiles page developer.apple.com/account/resources/profiles/list
  • Click the + sign to add a new profile which will take you to the "Register a New Provisioning Profile" page.
  • Select either macOS App Development or Mac App Store. Then click Continue.
  • Select your appID and continue.
  • Select your Mac App Distribution certificate and continue.
  • Give it a recognizable name like "AppleDevelopment" or "MacAppStore" then download it to your computer.
  • Double click the file to install it, then place it in the project's build directory. The file name would be AppleDevelopment.provisionprofile or MacAppStore.provisionprofile. This corresponds to the provisioningProfile key in the package.json file. Whatever you name it, during the build process it gets renamed to embedded.provisionprofile.
  • This is a binary file. If you want to read it's contents in XML format and you have X-Code's Command Line utilities installed, from the Terminal run the appropriate below command depending on the file name:
  • security cms -D -i build/AppleDevelopment.provisionprofile
  • security cms -D -i build/MacAppStore.provisionprofile

  • 10) Build development and distribution versions the App with electron-builder

  • For building the app there are a few options. We will use the Electron-Builder package. Install it as a development dependency:
  • npm install -D electron-builder
  • Build the development version of the app using the development version of package.json, the AppleDevelopment.provisionprofile, and the Development signing certificate.
  • electron-builder --mac
  • After testing the Development version of the app, build the distribution version. Use the distribution version of package.json, the MacAppStore.provisionprofile, and the Distribution and Mac Installer Distribution signing certificates.
  • electron-builder --mac

    11) Test the app

    Test the Development version of the app before building the distribution version. Make sure it works as expected.

    Then build the distribution version. This version will crash and give you a signing error if you try to open it on your computer. It must be re-signed by Apple to be able to run, which will only be possible after being downloaded from the Mac App Store. However, there are some checks you can make before submitting it to Apple.

  • You can test the signature if you have X-Code's CLI utilities installed. To check that your app is signed, go to the directory holding your app (e.g., MyApp/dist/mas) and enter the below command. If the app is signed it will display the details in the Terminal.
  • codesign --display --verbose=2 MyApp.app
  • To check that the .pkg file is signed enter the below command from the .pkg file's directory. If it is signed it will display the details in the Terminal.
  • pkgutil --check-signature MyApp-1.0.0.pkg
  • The Transporter app (see next section) has built-in checks on your app that will let you know about some (but not all) problems with your app. Simply load the app and send it to appstore connect. Don't worry, this doesn't submit it for review. That's a different step.
  • If you are getting errors you could start by making sure all the basics are working by using this process on a simple Hello World Electron app. The Github example app link at the top is to such an app. Just add in your developer and app datails and provisioning profile.

  • 12) Upload the pkg file

  • There are three different tools you can use to upload the pkg file: help.apple.com/app-store-connect.
  • The Transporter app can be downloaded free from the Mac App Store.
  • Open the Transporter app > Drag and drop your Electron app's pkg file into the Transporter app > Deliver.
  • If no errors it will send the app to your developer account.
  • Go to appstoreconnect.apple.com and click MyApps > Prepare for Submission > Build + icon > Select the uploaded file - Done
  • Note: the build may have a "Missing Compliance" warning next to it. If so, when you submit the app you will be asked about any encryption in your app, so just answer the questions given.
  • If you are ready to submit then hit submit for review.
  • If you decide to make additional changes before submitting for review, you have to load another .pkg file through the Transporter app with a different build number.

  • 13) Native Node NPM packages

    If you installed one or more native node modules as a local dependency in your app such as Sqlite3 to use a sqlite database, you will need to make two modifications to the package.json file. Native node modules are npm packages that include C or C++ code.

    First, to ensure your native dependencies match the electron version, add the below postinstall script.

    Second, their executable file(s) need to be code signed in addition to the overall app and the .pkg file. This presents a problem for MAS builds. Apple can't read signatures inside the built binary app and will throw a code signing error if you try to open the packaged app. To solve this, unpack the native node dependencies by adding asarUnpack to the mac key in your package.json file.
    Ref: Electron Docs: using native node modules.

      "scripts": {
        "postinstall": "electron-builder install-app-deps",
        ...
      },
      "build": {
        "mac": {
          "asarUnpack": [
            "**/*.node"
          ],
          ...
        }
      }
    

    That will pull all node executable files (which have the extension .node) out of your app's binary file (called app.asar) and into a sibling folder called app.asar.unpacked. Apple can then read the dependency's signing certificate.

    After building the app you can confirm that the .node files are signed:

  • Find the path to the .node executable file(s) in Finder by right clicking the built app in the dist/mas folder > Select "Show Package Contents" > Drill down to Contents/Resources/app.asar.unpacked/node_modules... until you find the executable file (ending in .node)
  • In the Terminal cd to the built app: cd dist/mas
  • Run the XCode CLI command:
    codesign --display --verbose=2 MyApp.app/Contents/Resources/app.asar.unpacked/node_modules/package_name/path/to/binary_file.node
    Sqlite3 example:
    codesign --display --verbose=2 MyApp.app/Contents/Resources/app.asar.unpacked/node_modules/sqlite3/lib/binding/napi-v3-darwin-x64/node_sqlite3.node


  • Comments: