Skip to content

Commit 8b93253

Browse files
committed
Add Mac Store guide
1 parent c23f806 commit 8b93253

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
* [React with TypeScript](guides/framework-integration/react-with-typescript.md)
6262
* [Vue 3](guides/framework-integration/vue-3.md)
6363
* [Developing with WSL](guides/developing-with-wsl.md)
64+
* [Mac and Windows Distribution](guides/mac-store.md)
6465

6566
## Advanced
6667

guides/mac-store.md

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
---
2+
description: 'This is a step-by-step guide to distributing your app either inside or outside of the Mac App Store, or to the Windows Store'
3+
---
4+
5+
# Mac and Windows Distribution
6+
7+
Follow the documentation on how to add Electron Forge to your project. This is fairly straight-forward, so I won't go into it.
8+
9+
Once this is done, a ```forge.config.js``` file will be 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.
10+
11+
```js
12+
const config = {
13+
asar: true,
14+
appBundleId: 'com.example.appname',
15+
appVersion: '1.0.0',
16+
buildVersion: '1.0.0',
17+
icon: './app'
18+
}
19+
```
20+
21+
```appBundleId```: At some point, you need to sign up for an Apple Developer Account and create an app. This is where you create a bundle Id that you then provide to Forge in this field.
22+
23+
```buildVersion```: This needs to be incremented each time you upload the app to Apple.
24+
25+
```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.
26+
27+
## Signing Configuration
28+
29+
If you want to submit your app to the Mac App Store, you will need to create the following certificates:
30+
31+
- Apple Development
32+
- Apple Distribution
33+
- Mac Installer Distribution
34+
35+
If you want to distribution your app outside of the App Store, you will need the following certificates:
36+
37+
- Developer ID Application
38+
- Developer ID Installer
39+
40+
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.
41+
42+
Once you have created these 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.
43+
44+
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.
45+
46+
Back to the config:
47+
48+
```js
49+
const osxSign = {
50+
binaries: [
51+
'./resources/bin/ffmpeg_intel_mac',
52+
'./resources/bin/ffmpeg_mac'
53+
],
54+
identity: 'Apple Development',
55+
platform: 'mas',
56+
type: 'development',
57+
provisioningProfile: 'development.provisionprofile',
58+
optionsForFile: (filePath) => {
59+
const entitlements = filePath.includes('.app/') ? 'entitlements.child.plist' : 'entitlements.plist';
60+
return {
61+
hardenedRuntime: false,
62+
entitlements
63+
}
64+
}
65+
}
66+
```
67+
68+
```binaries```: if your electron app calls any binaries, they need to be listed here so that they can be signed.
69+
70+
```identity```: the name of the certificate.
71+
72+
- App store development: Apple Development
73+
- App store distribution: Apple Distribution: FirstName LastName (TEAMID)
74+
- Outside distribution: Developer ID Application: FirstName LastName (TEAMID)
75+
76+
```platform```: for the app store it is ```mas``` and for outside the app store it is ```darwin```
77+
78+
```provisioningProfile```: the appropriate provisioning profile, as mentioned earlier.
79+
80+
```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.
81+
82+
The documentation fails to mention that you need to add logic to determine which set of entitlements to use. If you specify more entitlements then your app uses, it will probably be rejected by the review process.
83+
84+
For submission to the app store, ```hardenedRuntime``` should be false, but for distribution outside of the app store, it should be true.
85+
86+
Here is an example main entitlements file. Add or remove entitlements depending on the needs of your app.
87+
88+
```xml
89+
<?xml version="1.0" encoding="UTF-8"?>
90+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
91+
<plist version="1.0">
92+
<dict>
93+
<key>com.apple.security.app-sandbox</key>
94+
<true/>
95+
<key>com.apple.security.files.user-selected.read-write</key>
96+
<true/>
97+
<key>com.apple.security.files.bookmarks.app-scope</key>
98+
<true/>
99+
<key>com.apple.security.network.client</key>
100+
<true/>
101+
<key>com.apple.security.print</key>
102+
<true/>
103+
<key>com.apple.security.device.usb</key>
104+
<true/>
105+
<key>com.apple.security.files.downloads.read-write</key>
106+
<true />
107+
</dict>
108+
</plist>
109+
```
110+
111+
Here is an example child entitlements file.
112+
113+
```xml
114+
<?xml version="1.0" encoding="UTF-8"?>
115+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
116+
<plist version="1.0">
117+
<dict>
118+
<key>com.apple.security.app-sandbox</key>
119+
<true/>
120+
<key>com.apple.security.inherit</key>
121+
<true/>
122+
</dict>
123+
</plist>
124+
```
125+
126+
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.
127+
128+
For distribution outside of the app store, you will need to add ```osxNotarize``` to the packager config section.
129+
130+
```js
131+
const osxNotarize = {
132+
tool: 'notarytool',
133+
appleId: process.env.APPLE_ID,
134+
appleIdPassword: process.env.APPLE_ID_PASSWORD,
135+
teamId: process.env.TEAM_ID
136+
}
137+
```
138+
139+
```appleId```: usually the email address you used to create your Apple account.
140+
141+
```appleIdPassword```: a one-time password you can create. This is mentioned in the documentation. You create it via the Apple Developer website or something like that.
142+
143+
```teamId```: that set of characters inside the brackets at the end of your identity name.
144+
145+
## Other Packager Configuration
146+
147+
```js
148+
const other = {
149+
platform: 'mas',
150+
osxUniversal: {
151+
x64ArchFiles: '*_mac'
152+
},
153+
extraResource: [
154+
'./resources/bin',
155+
'./resources/app.db'
156+
],
157+
appCategoryType: 'public.app-category.utilities'
158+
};
159+
```
160+
161+
```platform```: same as inside the ```osxSign``` configuration.
162+
163+
```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.
164+
165+
```extraResources```: files that you want to copy across to the ```resources``` folder of the containerized environment.
166+
167+
```appCategoryType```: The category that is appropriate for your application.
168+
169+
## Other Configuration
170+
171+
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.
172+
173+
I don't think you need any makers just for testing. Maybe add this if nothing is created.
174+
175+
```js
176+
const makers = [
177+
{
178+
name: '@electron-forge/maker-zip',
179+
platforms: ['mas'],
180+
}
181+
];
182+
```
183+
184+
For distribution on the mac store you need to create a ```pkg``` file, so you can use this configuration:
185+
186+
```js
187+
const makers = [
188+
{
189+
name: '@electron-forge/maker-pkg',
190+
platform: ['mas'],
191+
config: {
192+
identity: '3rd Party Mac Developer Installer: FirstName LastName (TEAMID)'
193+
}
194+
}
195+
];
196+
```
197+
198+
For distribution outside of the app store, you should create a ```dmg``` file using this configuration:
199+
200+
```js
201+
const makers = [
202+
{
203+
name: '@electron-forge/maker-dmg',
204+
config: {
205+
format: 'ULFO'
206+
}
207+
}
208+
];
209+
```
210+
211+
For Windows, you don't need to do any of the above steps related to signing. You just have to add a maker like this:
212+
213+
```js
214+
const makers = [
215+
{
216+
name: '@electron-forge/maker-appx',
217+
config: {
218+
publisher: 'CN=UUID',
219+
publisherDisplayName: 'CompanyName',
220+
displayName: 'AppName',
221+
version: '1.0.0',
222+
identityName: 'CompanyName.AppName'
223+
}
224+
}
225+
];
226+
```
227+
228+
The uuid in the ```publisher``` field can be found on the Microsoft website that you use to create the app listing. The only trick is that Windows doesn't like dashes in any file or folder names.
229+
230+
## Package Configuration
231+
232+
Inside your ```package.json``` you might need to add a ```productName``` field. Here are some of the fields I have added, beyond what is already in the standard file.
233+
234+
```json
235+
{
236+
"productName": "AppName",
237+
"scripts": {
238+
"build": "./node_modules/.bin/esbuild main.js --bundle --platform=node --format=cjs --packages=external --outfile=bundle.js",
239+
"mac": "npm run make -- --arch=universal --platform=darwin",
240+
"mas": "npm run make -- --arch=universal --platform=mas"
241+
}
242+
}
243+
```
244+
245+
```npm run mac``` will create a ```dmg``` file for distribution outside of the app store, and ```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 just use ```npm run package```.
246+
247+
## Testing and Submission
248+
249+
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, and not just running like a normal mac application.
250+
251+
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 and therefore you have messed up one of the steps in the configuration section.
252+
253+
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.
254+
255+
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. You just need to fix up the help section.
256+
257+
If your app only has one window, the app should close completely when the window is closed.
258+
259+
Once everything is ready, you need to download Transporter from the mac app store and upload your ```pkg``` file.

0 commit comments

Comments
 (0)