- ElectronZola2020-02-20T00:00:00+00:00https://www.fullstackstanley.com/tags/electron/atom.xmlMaking an Electron App with Ember JS Part #4: Windows2020-02-20T00:00:00+00:002020-02-20T00:00:00+00:00https://www.fullstackstanley.com/articles/making-an-electron-app-with-ember-js-part-4-windows/<p>This is part four in the series of blog posts “Making an Electron App with Ember JS” where I go over how I built my app <a href="https://snipline.io">Snipline</a> for the web, Mac, Windows, and Linux.</p>
<span id="continue-reading"></span>
<p>This post assumes you’ve read the previous chapters, if you haven’t then I highly recommend you do.</p>
<h2 id="notes-for-building-electron-apps-for-windows">Notes for building Electron apps for Windows</h2>
<p>To build the app for Windows you will need access to a machine that runs Windows. </p>
<p>We'll be building the app for external distribution (Downloadable from the web). It’s possible to build for the Windows Store but I have not had experience doing this. Please leave a comment below if you’ve done this!</p>
<p>As with MacOS, it’s highly recommended that you code sign your releases. Without doing this users will see warnings when trying to install your application. I use Sectigo for my certificates which start at $166/year. I’ll go into more details on this process throughout the rest of the article.</p>
<h2 id="certificate-setup">Certificate Setup</h2>
<p>As mentioned, I use Sectigo for my code signing certificates which you can find <a href="https://sectigo.com/signing-certificates/code-signing">here</a>. After you’ve purchased a certificate you may have to wait a few days for it to be sent to you.</p>
<p>You’ll receive an email with a link to install the certificate. Make sure that you click this on the Windows machine that you’ll be using in Internet Explorer - <em>NOT</em> Microsoft Edge.</p>
<p>Once you have installed the certificate you’ll need to export it to a <code>.pfx</code> file for use with the Electron build process. To do this, <a href="https://support.sectigo.com/Com_KnowledgeDetailPage?Id=kA01N000000zFK0#ie_export_certificate">follow this guide from Sectigo</a>.</p>
<p>Make sure to give the certificate a strong password!</p>
<h2 id="configuring-the-app-to-build-for-windows">Configuring the app to build for Windows</h2>
<p>Once you have the Ember app set up on your Windows machine you’ll need to make a few tweaks to the <code>ember-electron/electron-forge-config.js</code> file.</p>
<p>First, add a function for getting the code signing password you used earlier.</p>
<pre data-lang="javascript" style="background-color:#2b303b;color:#c0c5ce;" class="language-javascript "><code class="language-javascript" data-lang="javascript"><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">getSigningPassword</span><span>() {
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.platform !== '</span><span style="color:#a3be8c;">win32</span><span>') {
</span><span> </span><span style="color:#b48ead;">return</span><span>;
</span><span> }
</span><span>
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.env.</span><span style="color:#bf616a;">CODESIGN_PASSWORD</span><span>) {
</span><span> </span><span style="color:#b48ead;">return </span><span>process.env.</span><span style="color:#bf616a;">CODESIGN_PASSWORD</span><span>;
</span><span> } </span><span style="color:#b48ead;">else </span><span>{
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">Codesigning password can not be found, release build will fail</span><span>');
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">To fix, set CODESIGN_PASSWORD</span><span>');
</span><span> }
</span><span>}
</span></code></pre>
<p>Add or update the <code>electronWinstallerConfig</code> object</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span> // ...
</span><span> "electronWinstallerConfig": {
</span><span> "name": "acme",
</span><span> "noMsi": true,
</span><span> "authors": 'acme',
</span><span> "exe": 'Shopper.exe',
</span><span> "title": "Shopper",
</span><span> "certificateFile": "<certificate location>",
</span><span> "certificatePassword": getSigningPassword(),
</span><span> "icon": path.join(rootPath, 'electron-assets', 'shopper.ico'),
</span><span> },
</span><span> // ...
</span></code></pre>
<p>There are a few values you will need to update: <code>name</code>, <code>authors</code>, <code>exe</code>, <code>title</code>, <code>certificateFile</code>, and <code>icon</code>.</p>
<p>The <code>ico</code> file needs to be a <code>256x256</code> icon. This will be what is used on Windows as your app icon.</p>
<p>The <code>certificateFile</code> needs to be updated to the location where you saved the exported certificate.</p>
<p>To build the application use the following command, replacing the password with your own.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">env</span><span> CODESIGN_PASSWORD=<codesign password> env ELECTRON_ENV=production ember electron:make</span><span style="color:#bf616a;"> --environment</span><span>=production
</span></code></pre>
<p>One thing to mention is that this command is temperamental on Windows. I’ve had several builds fail due to code signing failures only for it to work with no changes later on. If you do receive this error, make sure to keep trying a few more times.</p>
<h2 id="final-notes">Final Notes</h2>
<p>And there you have it! You now know how to build an Ember JS Electron application across three of the most popular desktop platforms.</p>
<p>There are several things that can be improved on and investigated, including working with more Linux environments, integrating release upgrades for Windows and MacOS, or using Electron without relying on Ember Electron. These are all topics for other articles, but after reading through this series you should have enough to get your started. </p>
<p>Enjoy!</p>
Making an Electron App with Ember JS Part #2.5: MacOS Notarisation2019-08-23T00:00:00+00:002019-08-23T00:00:00+00:00https://www.fullstackstanley.com/articles/making-an-electron-app-with-ember-js-part-2-5-macos-notarisation/<p>This is a quick, cheeky part two-point-five of the blog post series “Making an Electron App with Ember JS” where I go over how I built my app <a href="https://snipline.io">Snipline</a> for the web, Mac, Windows, and Linux.</p>
<span id="continue-reading"></span>
<p>This is a quick, cheeky part two-point-five of the blog post series “Making an Electron App with Ember JS” where I go over how I built my app <a href="https://snipline.io">Snipline</a> for the web, Mac, Windows, and Linux.</p>
<p>With the upcoming update to MacOS, 10.15 Catalina, it’s important to notarise your app or your users will not be able to install it! I did not go over this in the previous post but felt it needed addressing.</p>
<h2 id="what-is-notarisation">What is notarisation?</h2>
<p>Notarisation is a new feature to the MacOS ecosystem which is required for apps distributed outside of the App Store. This feature allows Apple to scan your software for malicious content so that users can be confident that your app is safe to use. Not only this, but if your signing key is exposed, you can contact Apple to disable unauthorised versions of your app from being opened.</p>
<p>You can read more about this on the <a href="https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution">Apple Developer website</a>.</p>
<h2 id="notarising-our-app">Notarising our app</h2>
<p>There a few changes that need to be made to notarise our app.</p>
<p>First, we need add <code>electron-notorize</code> to the <code>package.json</code></p>
<pre data-lang="diff" style="background-color:#2b303b;color:#c0c5ce;" class="language-diff "><code class="language-diff" data-lang="diff"><span style="color:#a3be8c;">+ "electron-notarize": "^0.1.1"
</span></code></pre>
<p>Next, in our <code>ember-electron/electron-forge.config.js</code> file we need to add the following code changes.</p>
<pre data-lang="diff" style="background-color:#2b303b;color:#c0c5ce;" class="language-diff "><code class="language-diff" data-lang="diff"><span style="color:#a3be8c;">+ const { notarize } = require('electron-notarize');
</span><span>// ...
</span><span>"osxSign": {
</span><span style="color:#bf616a;">- "identity": getCodesignIdentity()
</span><span style="color:#a3be8c;">+ "identity": getCodesignIdentity(),
</span><span style="color:#a3be8c;">+ "gatekeeper-assess": false,
</span><span style="color:#a3be8c;">+ "hardened-runtime": true,
</span><span style="color:#a3be8c;">+ "entitlements": path.join(rootPath, "ember-electron", "resources-darwin", "entitlements.plist"),
</span><span style="color:#a3be8c;">+ "entitlements-inherit": path.join(rootPath, "ember-electron", "resources-darwin", "entitlements.plist")
</span><span>},
</span><span>// ...
</span><span style="color:#a3be8c;">+ "hooks": {
</span><span style="color:#a3be8c;">+ postPackage: async function () {
</span><span style="color:#a3be8c;">+ if (process.platform !== 'darwin') {
</span><span style="color:#a3be8c;">+ console.log('Skipping notarization - not building for Mac');
</span><span style="color:#a3be8c;">+ return;
</span><span style="color:#a3be8c;">+ }
</span><span style="color:#a3be8c;">+
</span><span style="color:#a3be8c;">+ console.log('Notarizing...');
</span><span style="color:#a3be8c;">+
</span><span style="color:#a3be8c;">+ await notarize({
</span><span style="color:#a3be8c;">+ appBundleId: getBundleId(),
</span><span style="color:#a3be8c;">+ appPath: path.join(rootPath, "electron-out", "Shopper-darwin-x64", "Shopper.app"),
</span><span style="color:#a3be8c;">+ appleId: process.env.APPLE_ID,
</span><span style="color:#a3be8c;">+ appleIdPassword: process.env.APPLE_ID_PASSWORD
</span><span style="color:#a3be8c;">+ });
</span><span style="color:#a3be8c;">+ }
</span></code></pre>
<p>What do these changes do? Firstly, gatekeeper needs to be disabled for this to work correctly, and we need to specify an <code>entitlements.plist</code> file which we’ll create next. The <code>postPackage</code> hook deals with the notarisation. We check if we're compiling for MacOS, and then run the notarisation process. </p>
<p>You will need to change the <code>Shopper</code> references to your own app name. </p>
<p>We’re also specifying two new environment variables that will need to be passed to the build command, <code>APPLE_ID</code> and <code>APPLE_ID_PASSWORD</code>. This password is app specific and can be generated from your account at <a href="http://developer.apple.com">https://appleid.apple.com </a> - <strong>do not use your real Apple ID password here!</strong>. See these instructions for more details <a href="https://support.apple.com/en-us/HT204397">https://support.apple.com/en-us/HT204397</a></p>
<p>Next, it’s time to create the entitlements file, create <code>ember-electron/resources-darwin/entitlements.plist</code> and add the following</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span><?xml </span><span style="color:#bf616a;">version</span><span>="</span><span style="color:#a3be8c;">1.0</span><span>" </span><span style="color:#bf616a;">encoding</span><span>="</span><span style="color:#a3be8c;">UTF-8</span><span>"?>
</span><span><!DOCTYPE </span><span style="color:#bf616a;">plist</span><span> PUBLIC "</span><span style="color:#a3be8c;">-//Apple//DTD PLIST 1.0//EN</span><span>" "</span><span style="color:#a3be8c;">http://www.apple.com/DTDs/PropertyList-1.0.dtd</span><span>">
</span><span><plist </span><span style="color:#bf616a;">version</span><span>="</span><span style="color:#a3be8c;">1.0</span><span>">
</span><span> <dict>
</span><span> <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
</span><span> <true/>
</span><span> </dict>
</span><span></plist>
</span></code></pre>
<p>Now we can build the new release. Note that this may take some time, as the app gets checked on Apple’s servers. Remember to change the environment variables to your own.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">env</span><span> CODESIGN_IDENTITY="</span><span style="color:#a3be8c;">Developer ID Application: <Name> (<ID>)</span><span>" env BUNDLE_ID="</span><span style="color:#a3be8c;">io.exampledomain.desktop</span><span>" ELECTRON_ENV=production env APPLE_ID_PASSWORD=<password> env APPLE_ID=<appleid> ember electron:make</span><span style="color:#bf616a;"> --environment</span><span>=production
</span></code></pre>
<h2 id="the-dmg-file">The dmg file</h2>
<p>Code signing is no longer needed for <code>.dmg</code> files as Apple now checks the <code>.app</code> file within it. With this in mind, we can no longer use the <code>create-dmg</code> Javascript package as it automatically finds your certificate and applies it to the <code>.dmg</code> build.</p>
<p>Instead, we can use <code>electron-installer-dmg</code> which is already in our dependencies.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#65737e;"># Unzip the generated app
</span><span style="color:#bf616a;">unzip</span><span> Shopper-darwin-x64-x.x.x.zip
</span><span style="color:#65737e;"># Generate the dmg installer
</span><span style="color:#bf616a;">./node_modules/.bin/electron-installer-dmg</span><span> ./electron-out/make/Shopper.app Shopper</span><span style="color:#bf616a;"> --out</span><span>=./electron-out/make/</span><span style="color:#bf616a;"> --icon</span><span>=./electron-assets/shopper.icns</span><span style="color:#bf616a;"> --icon-size</span><span>=100
</span></code></pre>
<p>That’s all there is to it!</p>
<p>In the next chapter we’ll take a look at building for Linux!</p>
Making an Electron App with Ember JS Part #3: Linux2019-07-11T00:00:00+00:002019-07-11T00:00:00+00:00https://www.fullstackstanley.com/articles/making-an-electron-app-with-ember-js-part-3-linux/<p>This is part three in the series of blog posts “Making an Electron App with Ember JS” where I go over how I built my app <a href="https://snipline.io">Snipline</a> for the web, Mac, Windows, and Linux.</p>
<span id="continue-reading"></span>
<p>This post assumes you’ve read the previous chapters, if you haven’t then I highly recommend you do.</p>
<h2 id="building-for-linux">Building for Linux</h2>
<p>Building for Linux can be tricky. You have to take into account the following:</p>
<ul>
<li>What distributions do you want to support?</li>
<li>What deployment channels do you wish to use?</li>
</ul>
<p>Unfortunately it's not possible to go through every single combination. In this post we'll be focusing on building a <code>.deb</code> package for Debian based distros such as Ubuntu which can be downloaded and installed via the web.</p>
<h2 id="building-for-debian">Building for Debian</h2>
<p>A debian package is a file that can be easily downloaded from the internet, it's very easy to set up and it's relatively easy for users to install. The downside is that to update the users will need to download the latest package manually. </p>
<p>The upside of building <code>.deb</code> packages is that you can do this from any Debian based distro or even from MacOS with a couple of dependencies installed.</p>
<h3 id="requirements">Requirements</h3>
<p>Unlike the MacOS or Windows builds, you don't <em>need</em> to use Debian to build for Debian. If you're on MacOS you need to install the following dependencies via Homebrew.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">brew</span><span> install fakeroot dpkg
</span></code></pre>
<p>Update your <code>ember-electron-forge.js</code> to fill in the <code>electronInstallerDebian</code> section. Make sure to include your own applications information.</p>
<pre data-lang="diff" style="background-color:#2b303b;color:#c0c5ce;" class="language-diff "><code class="language-diff" data-lang="diff"><span> "make_targets": {
</span><span> // ...
</span><span> "linux": [
</span><span style="color:#a3be8c;">+ "deb",
</span><span> ]
</span><span> },
</span><span> // ...
</span><span> "electronInstallerDebian": {
</span><span style="color:#a3be8c;">+ "name": "shopper",
</span><span style="color:#a3be8c;">+ "productName": "Shopper",
</span><span style="color:#a3be8c;">+ "description": "A shopping list application",
</span><span style="color:#a3be8c;">+ "productDescription": "A shopping list application",
</span><span style="color:#a3be8c;">+ "icon": "electron-assets/shopper.png",
</span><span style="color:#a3be8c;">+ "bin": 'Shopper',
</span><span style="color:#a3be8c;">+ "desktopTemplate": path.join(rootPath, "ember-electron", "resources-linux", "desktop.ejs"),
</span><span style="color:#a3be8c;">+ "categories": [
</span><span style="color:#a3be8c;">+ "Utility"
</span><span style="color:#a3be8c;">+ ],
</span><span style="color:#a3be8c;">+ "homepage": "https://github.com/snipline/shopper"
</span><span> },
</span><span> // ...
</span></code></pre>
<p>Note that capitalisation is important in this file. See how <code>name</code> is lower case whereas <code>productName</code> and <code>bin</code> are uppercase.</p>
<p>For a list of valid categories <a href="https://standards.freedesktop.org/menu-spec/menu-spec-1.0.html#category-registry">see this page</a>.</p>
<p>If you've been paying attention to the code, you'll see that we need to make a "Desktop Template" (<code>.ejs.</code>) file in <code>ember-electron/resources-linux/desktop.ejs</code>. This is the contents of that file</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#65737e;">// ember-electron/resources-linux/desktop.ejs
</span><span>[</span><span style="color:#bf616a;">Desktop </span><span style="color:#ebcb8b;">Entry</span><span>]
</span><span><% </span><span style="color:#b48ead;">if </span><span>(</span><span style="color:#bf616a;">productName</span><span>) { %></span><span style="color:#bf616a;">Name</span><span>=<%= productName %>
</span><span><% } %><% if (description) { %></span><span style="color:#bf616a;">Comment</span><span>=<%= description %>
</span><span><% } %><% if (genericName) { %></span><span style="color:#bf616a;">GenericName</span><span>=<%= genericName %>
</span><span><% } %><% if (name) { %></span><span style="color:#bf616a;">Exec</span><span>=<%= name %> --</span><span style="color:#bf616a;">disable</span><span>-</span><span style="color:#bf616a;">gpu
</span><span style="color:#bf616a;">Icon</span><span>=<%= name %>
</span><span><% } %></span><span style="color:#bf616a;">Type</span><span>=</span><span style="color:#bf616a;">Application
</span><span style="color:#bf616a;">StartupNotify</span><span>=</span><span style="color:#d08770;">true
</span><span><% if (categories && categories.length) { %></span><span style="color:#bf616a;">Categories</span><span>=<%= categories.join('</span><span style="color:#a3be8c;">;</span><span>') %>;
</span><span><% } %><% if (mimeType && mimeType.length) { %></span><span style="color:#bf616a;">MimeType</span><span>=<%= mimeType.join('</span><span style="color:#a3be8c;">;</span><span>') %>;
</span><span><% } %>
</span></code></pre>
<p>The <code>desktop.ejs</code> file is the template for the application's <code>.desktop</code> file. This file takes the values we've just added to the Electron configuration and will add them to the generated Desktop file. The <code>.desktop</code> file then gets added to the user's local share directory when installed and makes the application available in the user's Applications menu.</p>
<p>Copy the icon (<code>shopper.png</code>) to <code>./electron-assets/shopper.png</code> (Or use your own icon) so that the build will have an icon - without this the build will fail.</p>
<p>If you're building this on MacOS, then before running the build command, we need to update any references to <code>process.platform</code> to <code>process.env.PLATFORM</code> in the Electron configuration file. This is because even though we're building for linux with the <code>--platform=linux</code> flag, Node still thinks the platform is <code>Darwin</code>.</p>
<p>Finally, to build for linux run the build command below.</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>env PLATFORM=linux env ELECTRON_ENV=production ember electron:make --environment=production --platform=linux
</span></code></pre>
<p>That's it! You should find a <code>.deb</code> file in your <code>make</code> directory.</p>
<h2 id="nativefier">Nativefier</h2>
<p>One final option for Linux, (and other Operating Systems if needed) is to use <a href="https://github.com/jiahaog/nativefier">Nativefier</a>. This essentially lets you wrap a web version of your app in Electron. The downside to this is the user needs internet access, you need to host the application online, and you get less control over other Electron settings. The upside is: it's very easy to implement and the user will always be on the latest version of your app.</p>
<h2 id="other-options">Other Options</h2>
<p>There are a lot of other options to build for Linux, including various package management systems such as apt, rpm and pacman. There's also Snapcraft, and Homebrew now supports Linux as well. Unfortunately, documentation for targeting these with Electron Forge is sparse. I'd encourage you to try them for yourself and see what you discover. Leave a comment if you get them working or even write an article! I'd love to see it.</p>
<p>In the next chapter we’ll take a look at building for Windows!</p>
Making an Electron App with Ember JS Part #1: Initial Setup2019-07-09T00:00:00+00:002019-07-09T00:00:00+00:00https://www.fullstackstanley.com/articles/making-an-electron-app-with-ember-js-part-1-initial-setup/<p>I work on the development of a tool called Snipline, a utility created to solve scratch my own itch, increasing my shell command productivity.</p>
<span id="continue-reading"></span>
<p>I first started building the web version of Snipline in Ember JS and soon discovered <a href="https://ember-electron.js.org">Ember Electron</a> which allowed me to create desktop clients of the web app for Windows, MacOS, and Linux really fast.</p>
<p>Although the addon does a great deal of work for you, there’s a lot of configuration that needs to be done as well - especially for releases. It’s taken me a long time as well as a lot of trial and error and wanted to share my discoveries with others.</p>
<p>This blog is part of an on-going series of posts that go into the details of building an Electron app in Ember JS. It will detail building for MacOS, Linux, and Windows, then I'll finish off with some closing thoughts and extra tips. </p>
<h2 id="initial-setup">Initial setup</h2>
<p>I’ve created a dummy app which you can <a href="https://github.com/snipline/shopper">download from Github</a> and follow along with. Of course, if you already have an Ember app ready to use, checkout a new branch and give it a try!</p>
<p>The app I’ve created is called Shopper and is a simple shopping list app. It lets you split groceries into different categories and keep track of the items you’ve added to your basket, as well as reorder and delete them.</p>
<p>It uses Ember Mirage for the backend storage - which is really convenient for development, but the data does not persist. If you wish to use this application for real then you will need to add your own backend API.</p>
<p>You’ll also need Yarn, Node (I’m using 10.15), and Ember CLI installed. After running <code>yarn</code>, you should be able view the web version with <code>ember serve</code>.</p>
<p><img src="https://f002.backblazeb2.com/file/ms-uploads/2019-07-08+14.34.27.gif" alt="" /></p>
<p>You will need MacOS to build the MacOS app and Windows to build the Windows app. You can build .deb (For Debian based operating systems) on MacOS with the correct tools installed, but it’s probably easier if you have access to a Linux machine. If you wish to build a Snapcraft package you will need Ubuntu 16.04.</p>
<h2 id="installing-electron">Installing Electron</h2>
<p>Run the following command to add Ember Electron to the app.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">ember</span><span> install ember-electron
</span></code></pre>
<p>This will install the addon and do the initial set up. This includes creating a new directory, <code>ember-electron</code> which is where we can place Electron related code, configuration and resources.</p>
<ul>
<li><code>main.js</code> - this file is the starting area for changing Electron app behaviour. For example, if you want to set the default window size, you can do it here.</li>
<li><code>electron-forge-config.js</code> - Under the hood, Ember Electron uses Electron Forge to build the app. This file is where we’ll put configuration related to building the app. This includes code signing for for MacOS/Windows.</li>
<li><code>resources/</code> - This is where you can place build related resources. We’ll place the Linux Desktop <code>.ejs</code> file in here as well as the app icon files.</li>
</ul>
<p>Without doing any modifications, let’s try running the Electron app in a development environment. Run the following command from the project root.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">ember</span><span> electron
</span></code></pre>
<p>You should be greeted with an app like the screenshot below (Or relatively similar if you’re on Windows/Linux).</p>
<p><img src="https://f002.backblazeb2.com/file/ms-uploads/Screenshot+2019-07-08+at+14.46.28.png" alt="" /></p>
<h2 id="configuring-the-app">Configuring the app</h2>
<p>So before we go onto building the app for release, there are a few tweaks that we should make and a few to take into consideration.</p>
<ul>
<li>How to change the default window size</li>
<li>(MacOS) Closing the app from the window and clicking the Dock icon doesn’t reopen the app.</li>
<li>How to set a minimum width/height for the app.</li>
<li>(MacOS) How to change the title bar style.</li>
<li>How to add items to the menu bar.</li>
</ul>
<p>To configure all of these we need to update the <code>ember-electron/main.js</code> file.</p>
<p>First of all, lets move the mainWindow stuff into it’s own function and call this function from the <code>ready</code> event.</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#bf616a;">app</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">ready</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#8fa1b3;">loadApp</span><span>();
</span><span>});
</span><span>
</span><span style="color:#65737e;">// Create a new variable for the main window
</span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">mainWindow </span><span>= </span><span style="color:#d08770;">null</span><span>;
</span><span>
</span><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">loadApp</span><span>() {
</span><span>
</span><span> </span><span style="color:#bf616a;">mainWindow </span><span>= new BrowserWindow({
</span><span> width: </span><span style="color:#d08770;">800</span><span>,
</span><span> height: </span><span style="color:#d08770;">600</span><span>,
</span><span> });
</span><span>
</span><span> </span><span style="color:#65737e;">// If you want to open up dev tools programmatically, call
</span><span> </span><span style="color:#65737e;">// mainWindow.openDevTools();
</span><span>
</span><span> </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">emberAppLocation </span><span>= '</span><span style="color:#a3be8c;">serve://dist</span><span>';
</span><span>
</span><span> </span><span style="color:#65737e;">// Load the ember application using our custom protocol/scheme
</span><span> </span><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#8fa1b3;">loadURL</span><span>(</span><span style="color:#bf616a;">emberAppLocation</span><span>);
</span><span>
</span><span> </span><span style="color:#65737e;">// If a loading operation goes wrong, we'll send Electron back to
</span><span> </span><span style="color:#65737e;">// Ember App entry point
</span><span> </span><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#bf616a;">webContents</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">did-fail-load</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#8fa1b3;">loadURL</span><span>(</span><span style="color:#bf616a;">emberAppLocation</span><span>);
</span><span> });
</span><span>
</span><span> </span><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#bf616a;">webContents</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">crashed</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">Your Ember app (or other code) in the main window has crashed.</span><span>');
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">This is a serious issue that needs to be handled and/or debugged.</span><span>');
</span><span> });
</span><span>
</span><span> </span><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">unresponsive</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">Your Ember app (or other code) has made the window unresponsive.</span><span>');
</span><span> });
</span><span>
</span><span> </span><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">responsive</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">The main window has become responsive again.</span><span>');
</span><span> });
</span><span>}
</span></code></pre>
<p>To change the default window size and the minimum window size look for the <code>loadApp</code> function. You can see the default <code>width</code> and <code>height</code> is already set. To set the minimum add the following parameters. We’ll also set it to centre the app by default here as well.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span> </span><span style="color:#bf616a;">minWidth:</span><span> 400,
</span><span> </span><span style="color:#bf616a;">minHeight:</span><span> 400,
</span><span> </span><span style="color:#bf616a;">center:</span><span> true,
</span></code></pre>
<p>If you’re on MacOS you can use the transparent title bar style which many apps prefer. If you do this, you will need to update your CSS to make the window draggable.</p>
<p>In the same <code>loadApp</code> method, add the following</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span> </span><span style="color:#bf616a;">titleBarStyle: </span><span>'</span><span style="color:#a3be8c;">hidden</span><span>',
</span></code></pre>
<p>Then in your app css (For the Shopper app this is <code>app/styles/app.css</code> add the following:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">html,</span><span> body {
</span><span> -webkit-app-region: drag;
</span><span>}
</span><span style="color:#bf616a;">input,</span><span> select, textarea, button, a {
</span><span> -webkit-app-region: no-drag;
</span><span>}
</span></code></pre>
<p>In MacOS, if you try pressing the red close icon in the app window and reopen from the Dock nothing will happen. To fix this we need add an event hook. Place this above the <code>loadApp</code> function</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#bf616a;">app</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">activate</span><span>', </span><span style="color:#b48ead;">function </span><span>() {
</span><span> </span><span style="color:#b48ead;">if </span><span>(</span><span style="color:#bf616a;">mainWindow </span><span>=== </span><span style="color:#d08770;">null</span><span>) {
</span><span> </span><span style="color:#8fa1b3;">loadApp</span><span>();
</span><span> }
</span><span>});
</span></code></pre>
<p>Add this code below the <code>mainWindow</code> definition in the <code>loadApp </code> function</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#bf616a;">mainWindow</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">closed</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#bf616a;">mainWindow </span><span>= </span><span style="color:#d08770;">null</span><span>;
</span><span>})
</span></code></pre>
<p>We can keep the Dock icon loaded when all windows are closed by preventing it from quitting in the <code>window-all-closed</code> event.</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#bf616a;">app</span><span>.</span><span style="color:#8fa1b3;">on</span><span>('</span><span style="color:#a3be8c;">window-all-closed</span><span>', () </span><span style="color:#b48ead;">=> </span><span>{
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.platform !== '</span><span style="color:#a3be8c;">darwin</span><span>') {
</span><span> </span><span style="color:#bf616a;">app</span><span>.</span><span style="color:#8fa1b3;">quit</span><span>();
</span><span> }
</span><span>});
</span></code></pre>
<p>For the sake of example, if you wish to modify the menu items (File, Help, etc) we can do this here as well. Note that I tend to only do this for production releases as it removes the Developer Inspector and other useful items. Put this inside the <code>loadApp</code> function below everything else and add a new variable called template near line 6.</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#65737e;">// Add Menu to require('electron');
</span><span style="color:#b48ead;">const </span><span>{ </span><span style="color:#bf616a;">app</span><span>, </span><span style="color:#bf616a;">BrowserWindow</span><span>, </span><span style="color:#bf616a;">protocol</span><span>, </span><span style="color:#bf616a;">Menu </span><span>} = </span><span style="color:#96b5b4;">require</span><span>('</span><span style="color:#a3be8c;">electron</span><span>');
</span><span style="color:#65737e;">// Add a new variable for the menu template.
</span><span style="color:#b48ead;">var </span><span style="color:#bf616a;">mainWindow</span><span>, </span><span style="color:#bf616a;">template </span><span>= </span><span style="color:#d08770;">null</span><span>;
</span><span style="color:#65737e;">// ...
</span><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">loadApp</span><span>() {
</span><span> </span><span style="color:#65737e;">//mainWindow = ...
</span><span> </span><span style="color:#b48ead;">if</span><span>(process.env.</span><span style="color:#bf616a;">ELECTRON_ENV </span><span>!== "</span><span style="color:#a3be8c;">development</span><span>") {
</span><span> </span><span style="color:#bf616a;">template </span><span>= [
</span><span> {
</span><span> label: "</span><span style="color:#a3be8c;">Edit</span><span>",
</span><span> submenu: [
</span><span> { label: "</span><span style="color:#a3be8c;">Undo</span><span>", accelerator: "</span><span style="color:#a3be8c;">CmdOrCtrl+Z</span><span>", selector: "</span><span style="color:#a3be8c;">undo:</span><span>" },
</span><span> { label: "</span><span style="color:#a3be8c;">Redo</span><span>", accelerator: "</span><span style="color:#a3be8c;">Shift+CmdOrCtrl+Z</span><span>", selector: "</span><span style="color:#a3be8c;">redo:</span><span>" },
</span><span> { type: "</span><span style="color:#a3be8c;">separator</span><span>" },
</span><span> { label: "</span><span style="color:#a3be8c;">Cut</span><span>", accelerator: "</span><span style="color:#a3be8c;">CmdOrCtrl+X</span><span>", selector: "</span><span style="color:#a3be8c;">cut:</span><span>" },
</span><span> { label: "</span><span style="color:#a3be8c;">Copy</span><span>", accelerator: "</span><span style="color:#a3be8c;">CmdOrCtrl+C</span><span>", selector: "</span><span style="color:#a3be8c;">copy:</span><span>" },
</span><span> { label: "</span><span style="color:#a3be8c;">Paste</span><span>", accelerator: "</span><span style="color:#a3be8c;">CmdOrCtrl+V</span><span>", selector: "</span><span style="color:#a3be8c;">paste:</span><span>" },
</span><span> { label: "</span><span style="color:#a3be8c;">Select All</span><span>", accelerator: "</span><span style="color:#a3be8c;">CmdOrCtrl+A</span><span>", selector: "</span><span style="color:#a3be8c;">selectAll:</span><span>" }
</span><span> ]
</span><span> },{
</span><span> label: '</span><span style="color:#a3be8c;">Help</span><span>',
</span><span> submenu: [
</span><span> {
</span><span> label: '</span><span style="color:#a3be8c;">Learn More</span><span>',
</span><span> </span><span style="color:#8fa1b3;">click </span><span>() { </span><span style="color:#96b5b4;">require</span><span>('</span><span style="color:#a3be8c;">electron</span><span>').</span><span style="color:#bf616a;">shell</span><span>.</span><span style="color:#8fa1b3;">openExternal</span><span>('</span><span style="color:#a3be8c;">https://dev.to/mitchartemis</span><span>') }
</span><span> }
</span><span> ]
</span><span> }];
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.platform === '</span><span style="color:#a3be8c;">darwin</span><span>') {
</span><span> </span><span style="color:#bf616a;">template</span><span>.</span><span style="color:#96b5b4;">unshift</span><span>({
</span><span> label: </span><span style="color:#bf616a;">app</span><span>.</span><span style="color:#8fa1b3;">getName</span><span>(),
</span><span> submenu: [
</span><span> {label: '</span><span style="color:#a3be8c;">Check for updates</span><span>', </span><span style="color:#8fa1b3;">click</span><span>() { </span><span style="color:#96b5b4;">require</span><span>('</span><span style="color:#a3be8c;">electron</span><span>').</span><span style="color:#bf616a;">shell</span><span>.</span><span style="color:#8fa1b3;">openExternal</span><span>(`</span><span style="color:#a3be8c;">https://dev.to/mitchartemis</span><span>`); }},
</span><span> {role: '</span><span style="color:#a3be8c;">about</span><span>'},
</span><span> {type: '</span><span style="color:#a3be8c;">separator</span><span>'},
</span><span> {role: '</span><span style="color:#a3be8c;">quit</span><span>'}
</span><span> ]
</span><span> })
</span><span> } </span><span style="color:#b48ead;">else </span><span>{
</span><span> </span><span style="color:#bf616a;">template</span><span>.</span><span style="color:#96b5b4;">unshift</span><span>({
</span><span> label: "</span><span style="color:#a3be8c;">File</span><span>",
</span><span> submenu: [
</span><span> {label: '</span><span style="color:#a3be8c;">Check for updates</span><span>', </span><span style="color:#8fa1b3;">click</span><span>() { </span><span style="color:#96b5b4;">require</span><span>('</span><span style="color:#a3be8c;">electron</span><span>').</span><span style="color:#bf616a;">shell</span><span>.</span><span style="color:#8fa1b3;">openExternal</span><span>(`</span><span style="color:#a3be8c;">https://dev.to/mitchartemis</span><span>`); }},
</span><span> {type: '</span><span style="color:#a3be8c;">separator</span><span>'},
</span><span> {role: '</span><span style="color:#a3be8c;">quit</span><span>'}
</span><span> ]
</span><span> })
</span><span> }
</span><span> </span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">menu </span><span>= </span><span style="color:#bf616a;">Menu</span><span>.</span><span style="color:#8fa1b3;">buildFromTemplate</span><span>(</span><span style="color:#bf616a;">template</span><span>)
</span><span> </span><span style="color:#bf616a;">Menu</span><span>.</span><span style="color:#8fa1b3;">setApplicationMenu</span><span>(</span><span style="color:#bf616a;">menu</span><span>)
</span><span> }
</span><span>}
</span></code></pre>
<p>There are a few things going on here, first we check if we’re in development mode, if we’re not then we create a Menu from our own template.</p>
<p>The <code>label</code> attribute allows us to specify the top level names and inside the <code>submenu</code> we place all of the menu options.</p>
<p>We can create links to external websites like so:</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span>{label: '</span><span style="color:#a3be8c;">Check for updates</span><span>', </span><span style="color:#8fa1b3;">click</span><span>() { </span><span style="color:#96b5b4;">require</span><span>('</span><span style="color:#a3be8c;">electron</span><span>').</span><span style="color:#bf616a;">shell</span><span>.</span><span style="color:#8fa1b3;">openExternal</span><span>(`</span><span style="color:#a3be8c;">https://dev.to/mitchartemis</span><span>`); }}
</span></code></pre>
<p>Create separators</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span>{type: '</span><span style="color:#a3be8c;">separator</span><span>'},
</span></code></pre>
<p>Use predefined functionality with <code>roles</code></p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span>{role: '</span><span style="color:#a3be8c;">about</span><span>'}
</span><span>{role: '</span><span style="color:#a3be8c;">quit</span><span>'}
</span></code></pre>
<p>And specify shortcuts for pre-existing methods as well.</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span>{ label: "</span><span style="color:#a3be8c;">Select All</span><span>", accelerator: "</span><span style="color:#a3be8c;">CmdOrCtrl+A</span><span>", selector: "</span><span style="color:#a3be8c;">selectAll:</span><span>" }
</span></code></pre>
<p>Now it’s time to <code>Ctrl+C</code> the current running app and re-run it to see the results.</p>
<p><img src="https://f002.backblazeb2.com/file/ms-uploads/Screenshot+2019-07-08+at+14.42.35.png" alt="" /></p>
<p>In MacOS you should be able to click-and-drag the whole app window (except form elements) and close and reopen the app from the dock. For all platforms there should now be a minimum of 400x400 window size.</p>
<h2 id="prepping-for-a-release">Prepping for a release</h2>
<p>If you’re using your own app, change the <code>electron-prebuild-compile</code> to use v3 rather than v4 in your <code>package.json</code> dev dependencies and run <code>yarn upgrade</code>.</p>
<p><code>"electron-prebuilt-compile": "3.0.13", </code>
If you don’t do this you will be unable to use the <code>electron make</code> command.</p>
<h2 id="the-app-icon">The app icon</h2>
<p>During development the Electron app uses the default Electron development icon, but when you build a production release you’re able to use your own icon.</p>
<p>The icon will need to be available for each platform.</p>
<ul>
<li><code>.icns</code> for MacOS</li>
<li>256x256 <code>.ico</code> for Windows</li>
<li>1024x1024 <code>.png</code> for Linux</li>
</ul>
<p>The MacOS <code>.icns</code> file can be made a few ways, but at the very least you’ll need a 1024x1024 transparent png to convert from.</p>
<p>I highly recommend the free Mac app, <a href="https://apps.apple.com/us/app/image2icon-make-your-icons/id992115977">Image2icon</a> (Also available in Setapp). Plug in your image and export to <code>.icns</code>. As a bonus, you can also use this to create your Windows <code>.ico</code> file, too — although this comes at a cost. There are plenty of free online <code>.png</code> to <code>.ico</code> converters out there.</p>
<p>If you’d rather make the <code>.icns</code> file manually, there’s an <a href="https://stackoverflow.com/a/20703594">excellent post on StackOverflow</a> on how to do that.</p>
<p>Once you have all the images place them in the <code>ember-electron/resources</code> directory. It’s really important to give them the same name. I’ve included the icon files for Shopper in the Github repository.</p>
<h2 id="version-number">Version number</h2>
<p>Make sure before building to update your version number! You can do this from the <code>~/package.json</code>. This will show in outputted build file and the MacOS About menu.</p>
<h2 id="what-we-ve-done-so-far">What we’ve done so far</h2>
<p>That’s all for part one. We’ve covered a lot of ground in a small amount of time, including integrating Electron into an Ember App, configuring the app for it’s first release, and going over some extra details such as creating icons and menu items.</p>
<p>In part two we’ll create the first MacOS release with code signing.</p>
<p><a href="/articles/making-an-electron-app-with-ember-js-part-2-macos">Click here to read part two.</a></p>
Making an Electron App with Ember JS Part #2: MacOS2019-07-09T00:00:00+00:002019-07-09T00:00:00+00:00https://www.fullstackstanley.com/articles/making-an-electron-app-with-ember-js-part-2-macos/<p>This is part two in the series of blog posts “Making an Electron App with Ember JS” where I go over how I built my app <a href="https://snipline.io">Snipline</a> for the web, Mac, Windows, and Linux.</p>
<span id="continue-reading"></span>
<p>This post assumes you’ve read part one, if you haven’t then I highly recommend it. Now, on with the show!</p>
<h2 id="building-for-macos">Building for MacOS</h2>
<p>Before building the app we need prepare it for code signing. For this you will need to have an Apple Developer Account which if you haven’t already, you can get from the <a href="http://developer.apple.com">Apple Developer website</a>. Note that this costs a yearly fee of $99.</p>
<p>Why is code signing important? I’m glad you asked! Code signing makes sure the files that your users download hasn’t been tampered with and comes from the developer that you expect. Without it, MacOS and Windows will go warn users about running the app and to a certain extent prevent them from doing so.</p>
<p>You should be able to follow along without code signing for educational purposes but for a production app I would highly recommend it.</p>
<p>Once you have the Developer account set up create a “Developer ID Application” certificate, download and install it on your Mac machine.</p>
<p>In <code>ember-electron/electron-forge-config.js</code> add the following:</p>
<pre data-lang="js" style="background-color:#2b303b;color:#c0c5ce;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#65737e;">// At the top
</span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">path </span><span>= </span><span style="color:#96b5b4;">require</span><span>('</span><span style="color:#a3be8c;">path</span><span>');
</span><span style="color:#b48ead;">const </span><span style="color:#bf616a;">rootPath </span><span>= </span><span style="color:#bf616a;">path</span><span>.</span><span style="color:#96b5b4;">join</span><span>('</span><span style="color:#a3be8c;">./</span><span>');
</span><span>
</span><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">getCodesignIdentity</span><span>() {
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.platform !== '</span><span style="color:#a3be8c;">darwin</span><span>') {
</span><span> </span><span style="color:#b48ead;">return</span><span>;
</span><span> }
</span><span>
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.env.</span><span style="color:#bf616a;">CODESIGN_IDENTITY</span><span>) {
</span><span> </span><span style="color:#b48ead;">return </span><span>process.env.</span><span style="color:#bf616a;">CODESIGN_IDENTITY</span><span>;
</span><span> } </span><span style="color:#b48ead;">else </span><span>{
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">Codesigning identity can not be found, release build will fail</span><span>');
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">To fix, set CODESIGN_IDENTITY</span><span>');
</span><span> }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">function </span><span style="color:#8fa1b3;">getBundleId</span><span>() {
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.platform !== '</span><span style="color:#a3be8c;">darwin</span><span>') {
</span><span> </span><span style="color:#b48ead;">return</span><span>;
</span><span> }
</span><span>
</span><span> </span><span style="color:#b48ead;">if </span><span>(process.env.</span><span style="color:#bf616a;">BUNDLE_ID</span><span>) {
</span><span> </span><span style="color:#b48ead;">return </span><span>process.env.</span><span style="color:#bf616a;">BUNDLE_ID</span><span>;
</span><span> } </span><span style="color:#b48ead;">else </span><span>{
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">bundle id can not be found, release build will fail</span><span>');
</span><span> </span><span style="color:#ebcb8b;">console</span><span>.</span><span style="color:#96b5b4;">log</span><span>('</span><span style="color:#a3be8c;">To fix, set BUNDLE_ID</span><span>');
</span><span> }
</span><span>}
</span><span>
</span><span style="color:#65737e;">// Replace electronPackagerConfig with this
</span><span>"</span><span style="color:#a3be8c;">electronPackagerConfig</span><span>": {
</span><span> "</span><span style="color:#a3be8c;">packageManager</span><span>": "</span><span style="color:#a3be8c;">yarn</span><span>",
</span><span> name: "</span><span style="color:#a3be8c;">Shopper</span><span>",
</span><span> icon: </span><span style="color:#bf616a;">path</span><span>.</span><span style="color:#96b5b4;">join</span><span>(</span><span style="color:#bf616a;">rootPath</span><span>, '</span><span style="color:#a3be8c;">ember-electron</span><span>', '</span><span style="color:#a3be8c;">resources</span><span>', '</span><span style="color:#a3be8c;">shopper</span><span>'),
</span><span> versionString: {
</span><span> CompanyName: '</span><span style="color:#a3be8c;">Acme Ltd</span><span>',
</span><span> FileDescription: '</span><span style="color:#a3be8c;">Shpoper for Desktop</span><span>',
</span><span> ProductName: '</span><span style="color:#a3be8c;">Shopper</span><span>',
</span><span> InternalName: '</span><span style="color:#a3be8c;">Shopper</span><span>'
</span><span> },
</span><span> "</span><span style="color:#a3be8c;">osxSign</span><span>": {
</span><span> "</span><span style="color:#a3be8c;">identity</span><span>": </span><span style="color:#8fa1b3;">getCodesignIdentity</span><span>()
</span><span> },
</span><span> "</span><span style="color:#a3be8c;">appBundleId</span><span>": </span><span style="color:#8fa1b3;">getBundleId</span><span>(),
</span><span> "</span><span style="color:#a3be8c;">appCategoryType</span><span>": "</span><span style="color:#a3be8c;">app-category-type=public.app-category.developer-tools</span><span>",
</span><span> },
</span><span>
</span><span style="color:#65737e;">// At the bottom of module.exports
</span><span>electronInstallerDMG: {
</span><span> title: '</span><span style="color:#a3be8c;">Shopper</span><span>',
</span><span> icon: </span><span style="color:#bf616a;">path</span><span>.</span><span style="color:#96b5b4;">join</span><span>(</span><span style="color:#bf616a;">rootPath</span><span>, '</span><span style="color:#a3be8c;">ember-electron</span><span>', '</span><span style="color:#a3be8c;">resources</span><span>', '</span><span style="color:#a3be8c;">shopper.icns</span><span>'),
</span><span>
</span><span> iconsize: </span><span style="color:#d08770;">100</span><span>,
</span><span> window: {
</span><span> size: {
</span><span> width: </span><span style="color:#d08770;">600</span><span>,
</span><span> height: </span><span style="color:#d08770;">571
</span><span> }
</span><span> }
</span><span>},
</span></code></pre>
<p>There’s one extra step before we can run. Code signing <a href="https://www.fullstackstanley.com/articles/making-an-electron-app-with-ember-js-part-2-5-macos-notarisation/">On a Mac</a> no longer allows any file in an app bundle to have an extended attribute containing a resource fork or Finder info. This will most likely apply to any assets that you’ve created and you can debug it by running</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">xattr -lr</span><span> .
</span></code></pre>
<p>In the Shopper app it only affects the newly created icons. We can fix this by running the following command. In your own apps you will need to use both commands to find and fix any assets. Without doing this your app will build, but code signing might fail.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">xattr -cr</span><span> ember-electron/resources
</span></code></pre>
<p>Now for the fun part. Take the following build command and update the <code>CODESIGN_IDENTITY</code> and <code>BUNDLE_ID</code> variables. The bundle ID should be a unique code, most people use their domain name in reverse with unique subdomain. </p>
<p>Run the command, go grab yourself a hot cup of tea, and when you’re back you should have a <code>.zip</code> in <code>electron-out/make/</code> file containing the app.</p>
<p><code>env CODESIGN_IDENTITY="Developer ID Application: <Name> (<ID>)" env BUNDLE_ID="io.exampledomain.desktop" ELECTRON_ENV=production ember electron:make --environment=production </code></p>
<p>Unzip it and run it, you should see the new app, dock icon and all!</p>
<h2 id="creating-the-dmg-installer">Creating the DMG installer</h2>
<p>An optional nice touch is to create a DMG file which will guide the user into moving your app into their <code>/Applications</code> directory.</p>
<p>For this I use an open source tool called <a href="https://github.com/sindresorhus/create-dmg">create-dmg</a>. It’s fairly simple to use and will pick up your code signing cert automatically.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#96b5b4;">cd</span><span> electron-out/make/
</span><span style="color:#bf616a;">rm -rf</span><span> Shopper.app
</span><span style="color:#bf616a;">unzip</span><span> Shopper-darwin-x64-0.1.0.zip
</span><span style="color:#bf616a;">create-dmg</span><span> Shopper.app ./
</span></code></pre>
<h2 id="what-we-ve-done-so-far">What we’ve done so far</h2>
<p>We’ve configured the Electron app to generate a code signed MacOS application and bundled it into an easy-to-install DMG file.</p>
<p>In the next chapter we’ll take a look at building for Linux!</p>