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.
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:
Development: used to sign apps for development and testing on machines that have been registered on the Apple Developer website.
Distribution: used to sign apps submitted to the Mac App Store.
Mac Installer Distribution: used to sign the Mac Installer Package in addition to the app itself.
You can create the signing certificates with either XCode or using the Keychain Access app
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 > If you have multiple apps select the relevant appleID and Team > Click the Manage Certificates button - This will show your list of certificates.
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 - this should display your certificates 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. Or go directly to https://developer.apple.com/account/resources/certificates/list
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:
Platform: MacOS
Bundle ID: Explicit. Apple recommends using a reverse-domain name style string (i.e., com.domainname.appname). This does not need to correspond with an actual website. This will need to match the appId property in the package.json file.
When done click continue, review it, then click register.
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:
Enter the name you want your app to appear on the Mac App Store as. Two apps can't have the same name so your preferred app name may be taken.
Your app needs a privacy policy URL and (in another section) a support URL.
Select the appropriate category for your app. See Category list.
Add a license agreement. You can use Apple's Standard License Agreement https://www.apple.com/legal/internet-services/itunes/dev/stdeula/
Fill out the Pricing and Availability section:
Select the price you want to charge for your app. Be aware that Apple keeps 30%.
You can also select specific countries to make your app available in. Default is the whole world.
Fill out the MacOS section:
For marketing you can add up to 10 screenshots and up to 3 15-30 second videos. Be aware that these must be in specific dimensions.
The App Store Icon is taken from your app's icon.icns file so you don't need to upload them separately.
Enter the version number using semantic versioning (e.g., starting with 1.0.0).
If your app requires any entitlements enter them here along with the explanation as to why you need that entitlement. The app will be rejected if you request entitlements you don't need. See the Entitlements section below for details.
Enter the support URL (required) and a marketing URL (optional).
When you upload your app (using the Transporter App - discussed below) you need to select it here.
You can add attachments that may be useful to the reviewer such as a video of how to use the app (can be any dimensions).
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.
Make a folder called icon.iconset (or any name that has .iconset as the extension) to hold the images.
Use Apple's naming convention for the image files (e.g., icon_512x512.png, icon_512x512@2x.png, etc.).
The @2x files are double the size stated in terms of pixels. However in terms of display, Apple just doubles the density of the pixels instead of doubling the width and height. As such, some of the files will be the same image dimensions but two different files (e.g., icon_256x256@2x.png and icon_512x512.png are two separate files but are the same image size). If you are simplifying the images at smaller sizes, make sure the @2x image is the same as the 1x, just at double the size.
When you get down to the small sizes such as icon_16x16.png you'll likely need to simplify the image, otherwise it will just look blurry.
Convert the iconset into an icns file in the Terminal from the directory holding the icon.iconset folder with the png images. Enter the iconutil command:
iconutil -c icns icon.iconset
Put the resulting icon.icns file into the build folder of your electron project.
7) package.json file
7a) Development version (for testing)
The values in italics need to be changed to your info.
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 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.
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):
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
9a) Generate the AppleDevelopment Provisioning Profile
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. Then click Continue.
Select your appID, then click continue.
Give it a recognizable name like "AppleDevelopment", 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. 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 in the project folder run the below command:
Click the + sign to add a new profile which will take you to the "Register a New Provisioning Profile" page.
Select Mac App Store. Then click Continue.
Select your appID, then click continue.
Select your Mac App Distribution certificate, then click continue.
Give it a recognizable name like "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 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 in the project folder run the below command:
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.
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.
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