-
Notifications
You must be signed in to change notification settings - Fork 167
Add Mac Store guide #251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v6
Are you sure you want to change the base?
Add Mac Store guide #251
Changes from all commits
8b93253
b300fbd
34f6e65
f4dcf49
78952d1
fdd40c6
417ff35
342aa60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,12 +29,18 @@ Although Electron does not integrate tightly with the IDE itself, Xcode is a hel | |
|
|
||
| Code signing certificates for macOS apps can only be obtained through Apple by purchasing a membership to the [Apple Developer Program](https://developer.apple.com/programs/). | ||
|
|
||
| To sign Electron apps, you may require two separate certificates: | ||
| If you want to submit your app to the Mac App Store, you will need to create the following certificates: | ||
|
|
||
| * The **Developer ID Installer** certificate is for apps distributed to the Mac App Store. | ||
| * The **Developer ID Application** certificate is for apps distributed outside the Mac App Store. | ||
| - Apple Development | ||
| - Apple Distribution | ||
| - Mac Installer Distribution | ||
|
|
||
| Once you have an Apple Developer Program membership, you first need to install them onto your machine. We recommend [loading them through Xcode](https://help.apple.com/xcode/mac/current/#/dev3a05256b8). | ||
| If you want to distribution your app outside of the App Store, you will need the following certificates: | ||
|
|
||
| - Developer ID Application | ||
| - Developer ID Installer | ||
|
|
||
| All of these certificates should be created through Xcode after you have signed up for an Apple Developer Account. If you have created them any other way, you will have to delete them. | ||
|
|
||
| {% hint style="success" %} | ||
| **Verifying your certificate is installed** | ||
|
|
@@ -46,6 +52,12 @@ security find-identity -p codesigning -v | |
| ``` | ||
| {% endhint %} | ||
|
|
||
| ### Creating provisioning profiles | ||
|
|
||
| Once you have created the certificates, you need to go to your Apple Developer Account and create provisioning profiles. If you are submiting your app to the app store, you will need a Development profile and a Distribution profile. If you are submiting it outside of the app store, you will need a profile for the ```Developer ID Application``` certificate. | ||
|
|
||
| You need to download these after creating them and double clicking them to install them on your computer. Not all of them can be installed locally, but just double-click on them anyway. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think guides should tell readers to do something "anyway". Either its required, or we can leave it out.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I can't remember which ones do and don't need double clicking. If you double click one of them, it tells you it can't be installed or something like that. |
||
|
|
||
| ## Configuring Forge | ||
|
|
||
| In Electron Forge, macOS apps are signed and notarized at the **Package** step by the `electron-packager` library. There is a separate option within your Forge `packagerConfig` for each one of these settings. | ||
|
|
@@ -62,44 +74,98 @@ To enable code signing on macOS, ensure that `packagerConfig.osxSign` exists in | |
| ```javascript | ||
| module.exports = { | ||
| packagerConfig: { | ||
| osxSign: {} // object must exist even if empty | ||
| osxSign: { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any reason not to recommend the empty configuration object as the default anymore?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think there is a case where it would work with no options.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just tested myself on
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mmm.... I don't think it is that simple. Anyway, there would need to be some kind of examples, which is the foundation of any good documentation. Leaving it empty and simply describing the fields is pointless. Either way, I will make the edits I agree with and you can do the rest as you see fit. I will do that now. |
||
| binaries: [ | ||
| './resources/bin/ffmpeg_intel_mac', | ||
| './resources/bin/ffmpeg_mac' | ||
| ], | ||
| identity: 'Apple Development', | ||
| platform: 'mas', | ||
| type: 'development', | ||
| provisioningProfile: 'development.provisionprofile', | ||
| optionsForFile: (filePath) => { | ||
| const entitlements = filePath.includes('.app/') ? 'entitlements.child.plist' : 'entitlements.plist'; | ||
| return { | ||
| hardenedRuntime: false, | ||
| entitlements | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| ``` | ||
| {% endcode %} | ||
|
|
||
| The `osxSign` config comes with defaults that work out of the box in most cases, so we recommend you start with an empty configuration object. | ||
| ```binaries```: if your electron app calls any binaries, they need to be listed here so that they can be signed. | ||
|
|
||
| For a full list of configuration options, see the [`OsxSignOptions`](https://js.electronforge.io/modules/\_electron\_forge\_shared\_types.InternalOptions.html#OsxSignOptions) type in the Forge API docs. For more detailed information on how to configure these options, see the [`@electron/osx-sign` documentation](https://github.com/electron/osx-sign). | ||
| ```identity```: the name of the certificate. | ||
|
|
||
| #### Customizing entitlements | ||
| - App store development: Apple Development | ||
| - App store distribution: Apple Distribution: FirstName LastName (TEAMID) | ||
| - Outside distribution: Developer ID Application: FirstName LastName (TEAMID) | ||
|
|
||
| A common use case for modifying the default `osxSign` configuration is to customize its entitlements. In macOS, **entitlements** are privileges that grant apps certain capabilities (e.g. access to the camera, microphone, or USB devices). These are stored within the code signature in an app's executable file. | ||
| ```platform```: for the app store it is ```mas``` and for outside the app store it is ```darwin``` | ||
|
|
||
| By default, the `@electron/osx-sign` tool comes with a set of entitlements that should work on both MAS or direct distribution targets. See the complete set of default entitlement files [on GitHub](https://github.com/electron/osx-sign/tree/main/entitlements). | ||
| ```provisioningProfile```: the appropriate provisioning profile, as mentioned earlier. | ||
|
|
||
| {% code title="forge.config.js" %} | ||
| ```javascript | ||
| module.exports = { | ||
| // ... | ||
| packagerConfig: { | ||
| // ... | ||
| osxSign: { | ||
| optionsForFile: (filePath) => { | ||
| // Here, we keep it simple and return a single entitlements.plist file. | ||
| // You can use this callback to map different sets of entitlements | ||
| // to specific files in your packaged app. | ||
| return { | ||
| entitlements: 'path/to/entitlements.plist' | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| // ... | ||
| }; | ||
| ```optionsForFile```: for distribution outside of the app store, you may be able to rely on the defaults if you app doesn't need any extra entitlements. For the app store, you will definitely need to provide this. | ||
|
|
||
| Your list of entitlements should contain only the ones your application needs. Adding extraneous entitlements could be cause for rejection during the review process. | ||
|
|
||
| For submission to the app store, ```hardenedRuntime``` should be false, but for distribution outside of the app store, it should be true. | ||
|
|
||
| For a full list of configuration options, see the [`OsxSignOptions`](https://js.electronforge.io/modules/\_electron\_forge\_shared\_types.InternalOptions.html#OsxSignOptions) type in the Forge API docs. For more detailed information on how to configure these options, see the [`@electron/osx-sign` documentation](https://github.com/electron/osx-sign). | ||
|
|
||
| #### Entitlements | ||
|
|
||
| In macOS, **entitlements** are privileges that grant apps certain capabilities (e.g. access to the camera, microphone, or USB devices). These are stored within the code signature in an app's executable file. | ||
|
|
||
| Here is an example main entitlements file. Add or remove entitlements depending on the needs of your app. | ||
|
|
||
| {% code title="entitlements.plist" %} | ||
| ```xml | ||
| <?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.files.user-selected.read-write</key> | ||
| <true/> | ||
| <key>com.apple.security.files.bookmarks.app-scope</key> | ||
| <true/> | ||
| <key>com.apple.security.network.client</key> | ||
| <true/> | ||
| <key>com.apple.security.print</key> | ||
| <true/> | ||
| <key>com.apple.security.device.usb</key> | ||
| <true/> | ||
| <key>com.apple.security.files.downloads.read-write</key> | ||
| <true /> | ||
| </dict> | ||
| </plist> | ||
| ``` | ||
| {% endcode %} | ||
|
|
||
| Here is an example child entitlements file. | ||
|
|
||
| {% code title="entitlements.child.plist" %} | ||
| ```xml | ||
| <?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> | ||
| ``` | ||
| {% endcode %} | ||
|
|
||
| Forge will add additional keys related to your provisioning profile. You should remove the ```app-sandbox``` key in both files when creating the set of entitlements you want to use outside of the app store, as that version does not run in a sandbox. | ||
|
|
||
| For further reading on entitlements, see the following pages in Apple developer documentation: | ||
|
|
||
| * [Entitlements](https://developer.apple.com/documentation/bundleresources/entitlements) | ||
|
|
@@ -127,9 +193,9 @@ There are two mandatory fields for `osxNotarize` if you are using this strategy: | |
|
|
||
| | Field | Type | Description | | ||
| | ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `appleId` | string | Apple ID associated with your Apple Developer account | | ||
| | `appleIdPassword` | string | App-specific password | | ||
| | `teamId` | string | The Apple Team ID you want to notarize under. You can find Team IDs for team you belong to by going to [`https://developer.apple.com/account/#/membership`](https://developer.apple.com/account/#/membership) | | ||
| | `appleId` | string | Usually the email address you used to create your Apple Developer account. | | ||
| | `appleIdPassword` | string | A one-time password that can be create via the Apple Developer website. | | ||
| | `teamId` | string | The Apple Team ID you want to notarize under. It is the set of characters inside the brackets at the end of your identity name. | | ||
|
Comment on lines
+196
to
+198
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old descriptions were more helpful and used the terminology as used by Apple.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not from my experience. As the documentation was wrong in many places, I can't see how the author could know which is the best or not best. It wasn't possible to submit to the MAS using the documentation. |
||
|
|
||
| {% code title="forge.config.js" %} | ||
| ```javascript | ||
|
|
@@ -138,6 +204,7 @@ module.exports = { | |
| packagerConfig: { | ||
| // ... | ||
| osxNotarize: { | ||
| tool: 'notarytool', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this the default so should be left out |
||
| appleId: process.env.APPLE_ID, | ||
| appleIdPassword: process.env.APPLE_PASSWORD, | ||
| teamId: process.env.APPLE_TEAM_ID | ||
|
|
@@ -225,6 +292,7 @@ module.exports = { | |
| packagerConfig: { | ||
| osxSign: {}, | ||
| osxNotarize: { | ||
| tool: 'notarytool', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this the default so should be left out |
||
| appleId: process.env.APPLE_ID, | ||
| appleIdPassword: process.env.APPLE_PASSWORD, | ||
| teamId: process.env.APPLE_TEAM_ID | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| --- | ||
| description: 'This is a step-by-step guide to distributing your app via the Mac App Store' | ||
| --- | ||
|
|
||
| # App Store | ||
|
|
||
| Follow the documentation on how to add Electron Forge to your project so that a ```forge.config.js``` file is created in your project's root folder. You can convert this to ESM syntax if you want, but if you are creating a universal application, your project will have to be converted back into CJS for the final publishing step as this part of the Electron Forge toolchain does not yet support ESM. | ||
|
|
||
| {% code title="forge.config.js" %} | ||
| ```js | ||
| module.exports = { | ||
| packagerConfig: { | ||
| asar: true, | ||
| appBundleId: 'com.example.appname', | ||
| appVersion: '1.0.0', | ||
| buildVersion: '1.0.0', | ||
| icon: './app', | ||
| osxSign: {}, | ||
| platform: 'mas', | ||
| osxUniversal: { | ||
| x64ArchFiles: '*_mac' | ||
| }, | ||
| extraResource: [ | ||
| './resources/bin', | ||
| './resources/app.db' | ||
| ], | ||
| appCategoryType: 'public.app-category.utilities' | ||
| } | ||
| } | ||
| ``` | ||
| {% endcode %} | ||
|
|
||
| ```appBundleId```: When you create an app from within the Apple Developer website, you will be asked to create a Bundle ID which should then be supplied to this field. | ||
|
|
||
| ```buildVersion```: This needs to be incremented each time you upload the app to Apple. | ||
|
|
||
| ```icon```: You can find templates online that provide the right dimensions for the Mac app icon. You need to create rounded corners and provide some space around the icon. | ||
|
|
||
| ```osxSign```: The code signing configuration. Following the instructions [here](code-signing/code-signing-macos.md) | ||
|
|
||
| ```platform```: same as inside the ```osxSign``` configuration. | ||
|
|
||
| ```osxUniversal```: only necessary if you have a mixture of intel and apple silicon binary files. You can avoid this by using ```lipo``` to combine these binaries into a single file if you want. | ||
|
|
||
| ```extraResources```: files that you want to copy across to the ```resources``` folder of the containerized environment. | ||
|
|
||
| ```appCategoryType```: The category that is appropriate for your application. | ||
|
|
||
| ## Other Configuration | ||
|
|
||
| After the packager configuration, there is the ```maker``` configuration and some types of configuration that you can just leave as they are unless you have a use for them. | ||
|
|
||
| For distribution on the Mac store you need to create a ```pkg``` file. | ||
|
|
||
| {% code title="forge.config.js" %} | ||
| ```js | ||
| module.exports = { | ||
| packagerConfig: {}, | ||
| makers: [ | ||
| { | ||
| name: '@electron-forge/maker-pkg', | ||
| platform: ['mas'], | ||
| config: { | ||
| identity: '3rd Party Mac Developer Installer: FirstName LastName (TEAMID)' | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
| {% endcode %} | ||
|
|
||
| ## Package Configuration | ||
|
|
||
| Inside your ```package.json``` add a ```productName``` field. The scripts mentioned below can help you bundle and package your code. | ||
|
|
||
| {% code title="package.json" %} | ||
| ```json | ||
| { | ||
| "productName": "AppName", | ||
| "scripts": { | ||
| "build": "./node_modules/.bin/esbuild main.js --bundle --platform=node --format=cjs --packages=external --outfile=bundle.js", | ||
| "mas": "npm run make -- --arch=universal --platform=mas" | ||
| } | ||
| } | ||
| ``` | ||
| {% endcode %} | ||
|
|
||
| ```npm run mas``` will create a ```pkg``` file for distribution inside the app store, assuming you have used the appropriate Forge configuration. For testing, you don't need to use any of the ```makers``` so you can simply use ```npm run package```. | ||
|
|
||
| ## Testing and Submission | ||
|
|
||
| To test your application before submitting it to the App Store, you need to make sure you are actually running inside an App Store sandbox. | ||
|
|
||
| App containers are located in ```~/Library/Containers```. If there is not a folder named after your app there, then your app is not running inside a container. This means that a configuration or signing issue has occured. | ||
|
|
||
| Before packaging your application, you should also check that every file has user read permissions, and that every folder has user read and user execute permissions. Every binary should have user read and user execute permissions as well. | ||
|
|
||
| You will also need a working help section in the menu of your app. The help section can link to a website. The Electron documentation provides a basic template that you can copy and paste to make sure you have the right menu items. It is only the help section that needs to be altered. | ||
|
|
||
| If your app only has one window, the app should close completely when the window is closed. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not what the minimal-repro example project uses. It would be good to remain consistent there.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Umm... this is required to successfully submit the app on MAS, which is what the guide is for.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an Apple guideline we can reference here? I skimmed https://developer.apple.com/app-store/review/guidelines/ and might have missed it but didn't see any mention about this particular rule.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I can't find anything either but my app was rejected because of this. The way that MacOS works is that if the app is a single window utility-based application like a calculator, then it closes completely when you close. If it can be used to spawn lots of new windows (document-based), it stays open. The problem is, what about apps that have background tasks running but are single window? Mine was in this category, yet it was still rejected. Maybe the reviewer made a mistake. The assumption I took from his review was that anything that doesn't launch multiple windows should close completely.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is also specifically what he said. |
||
|
|
||
| Once everything is ready, you need to download [Transporter](https://apps.apple.com/us/app/transporter/id1450874784?mt=12) from the Mac App Store and upload your ```pkg``` file. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is true, you can create them on the developer.apple.com website, download and install them. The removed link has information on that, so it's worth keeping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I tried this, it did not work. The only way that worked was via Xcode.