How to embed a mac app extension in an Electron app? - node.js

I'm trying to embed a Finder Sync extension written in Swift in my app written with Electron. How can I manage to make them work together and communicate with each other? I have read the Apple documentation but it only explains how to add a target to a native application. I also noticed that I can manually inject the .appex compiled file (produced by XCode) in the application Plugins folder using electron builder.
How can I develop and test the extension in XCode and embed it correctly in a custom Electron app?
Any suggestion?
Thank you very much for any suggestion

Create PlugIns folder in your Electron root folder.
Copy the .appex file into PlugIns folder.
If you are using electron-builder, modify the package.json file - add:
"extraFiles": ["PlugIns/"] in the "mac" section.
Build. The Contents of your app package will contain the PlugIns folder and your appex file inside, and the appex will get loaded into your app's process.

How to embed a mac app extension in an Electron app?
I would compile it as an independent binary and include it in some dir to be executed from the electron app using child_process.execFile
You can use arguments when executing the binary with execFile, here is an example (using promise)
const util = require('util');
const execFile = util.promisify(require('child_process').execFile);
async function FinderSyncExtPlugin(ARGUMENTS) {
const { stdout } = await execFile('YourBinary', ARGUMENTS);
console.log(stdout);
}
FinderSyncExtPlugin(['argument1','argument2','...']);
You could then use the stdout to know the status/result of the requested operation.

Related

NodeJS open local file using the default preview

I am trying to create a desktop app using electron which includes a function where a user can open a desired file saved in the local storage. Sine I am using MacOS, I want to use nodejs to be able to open the file (e.g. PDF doc) in the default preview software. is there any way to do this?
Thank you in advance.
A portable way is to make use of the Electron's shell API, specifically the shell.openPath method:
const { shell } = require('electron');
shell.openPath("/fullpath/to/file");
It is available both in the main process and in the renderer process, and it can also be used to open a folder in the Finder.
if anyone is wondering I found a way of using
const { exec } = require('child_process')
exec('open ~/path/to/file')
this used command shell (terminal) to open it. If there is any other better way to do it please let us know!

AppImage from electron-builder with file system not working

I’m using Electron Builder to compile my Electron app to an .AppImage file, and I’m using the fs module to write to an .json file, but it’s not working in the appimage format (it’s working fine when I have the normal version not made with Electron Builder). I can still read from the file.
The code (preload):
setSettings: (value) => {fs.writeFileSync(path.join(__dirname, "settings.json"), JSON.stringify(value), "utf8")}
The code (on the website):
api.setSettings(settings);
The project: https://github.com/Nils75owo/crazyshit
That's not a problem with AppImage or Electron Builder but with the way you're packaging your app. Since you didn't post your package.json*, I can only guess what's wrong, but probably you haven't changed Electron Builder's default behaviour regarding packing your application.
By default, Electron Builder compiles your application, including all resources, into a single archive file in the ASAR format (think of it like the TAR format). Electron includes a patched version of the fs module to be able to read from the ASAR file, but writing to it is obviously not supported.
You have two options to mitigate this problem: Either you store your settings somewhere in the user's directory (which is the way I'd go, see below) or you refrain from packing your application to an ASAR file, but that will leave all your JavaScript code outside the executable in a simple folder. (Note that ASAR is not capable of keeping your code confidential, because there are applications which can extract such archives, but it makes it at least a little harder for attackers or curious eyes to get a copy of your code.)
To disable packing to ASAR, simply tell Electron Builder that you don't want it to compile an archive. Thus, in your package.json, include the following:
{
// ... other options
"build": {
// ... other build options
"asar": false
}
}
However, as I mentioned above, it's probably wiser to store settings in a common place where advanced users can actually find (and probably edit, mostly for troubleshooting) them. On Linux, one such folder would be ~/.config, where you could create a subdirectory for your application.
To get the specific application data path on a cross-platform basis, you can query Electron's app module from within the main process. You can do so like this:
const { app } = require ("electron"),
path = require ("path");
var configPath;
try {
configPath = path.join (app.getPath ("appData"), "your-app-name");
} catch (error) {
console.error (error);
app.quit ();
}
If you however have correctly set your application's name (by using app.setName ("...");), you can instead simply use app.getPath ("userData"); and omit the path joining. Take a look at the documentation!
For my Electron Applications, I typically choose to store settings in a common hidden directory (one example for a name could be the brand under which you plan to market the application) in the user's home directory and I then have separate directories for each application. But how you organise this is up to you completely.
* For the future, please refrain from directing us to a GitHub repository and instead include all information (and code is information too) needed to replicate/understand your problem in your question. That'd save us a lot of time and could potentially get you answers faster. Thanks!

How to expose a module from an Electron app to an external module

I am creating an Electron app and am using electron-builder to package and build the app. Users are able to make plugins for the app (plugins can be node modules with their own dependencies and such). Everything is working fine except the part of the app for exposing the app's API to the plugin.
I created a module in the app for handling the plugins "Plugin-handler" that imports the plugin and also exposes the API to the plugin (The app's API is just a set of functions and it is bundled with the app).
The dilemma is that the user should be able to place a plugin anywhere on their machine and the app does not know the path before the build. Consequently, I excluded the "plugin-handler" module in the Electron-builder config so it does not bundle with the Webpack. Now I need to find the right way to expose the API to the plugin.
Here is how I am doing it now, to load the plugins and passing the API:
// In the Plugin-handler module
const API = require('api')
const plugin = require('path-to-plugin')( API )
path-to-plugin is added by the user in the app when they import their plugin.
As seen above, currently I pass the API to the plugin as an argument, which is not ideal, instead, I need a way to exposing the API module (or any other module that is bundled in the APP) to the plugin so users can access it in their plugin like below:
// In the plugin
const { arg1, arg2,... } = require('api')
I've seen apps doing this, and allowing users to access their API in their plugins, but since I am new to all this, I may be doing things wrong, so please do be kind, and thank you for the help!
I drew a simple chart for better portraying the question:

Electron Node.js node localstorage osx mkdir permission denied

I am working with Electron and Node.js. We have developed an application that works fine on windows and as a requirement had to package it for mac os. I packaged the application using electron-packager, the packaging process completes and package is generated. Double clicking it throws an error that permission denied for mkdir, as i am using node localstorage to maintain some settings on the user's local machine. somehow mac doesn't local storage to create folder in the root of the application. Any help in this matter will be great. Thanks
First off, is the code in question in the main process or in a renderer process? If it is the latter, you don't need to use 'node-localstorage', because you can use the renderer's native LocalStorage. If you are in the main process, then you need to provide your own storage strategy so using 'node-localstorage' is a viable option.
In any case, you need to carefully consider where to store the data; for starters, let's look at where Electron's renderer processes would store its LocalStorage data: this differs based on the OS, but you can get and set the paths using the app module -- the path in question is userData, which on OS X would default to ~/Library/Application Support/<App Name>. Electron uses that folder to persist cookies, caches, LocalStorage etc. so I would suggest using that folder as well. (Otherwise, refer to XDG defaults for good defaults)
What your example above was trying to do is store your 'errorLogDb' in the current working directory, which might depend on your OS, where your App is installed, how you executed it, etc.
Finally, it's a good idea to differentiate between your 'production' app and your app during development and testing, because you might not want to use the same storage folders for every environment. In any case, just writing to './errorLogDb' is likely to cause lots of headaches so I'd be thankful for the permission denied error.
this strategy worked for me:
const { LocalStorage } = require('node-localstorage');
let ls;
mb.on('ready', () => {
let prefsPath = mb.app.getPath('userData') + '/prefs';
ls = new LocalStorage(prefsPath);
loadPrefs();
});
mb.on('after-create-window', () => { /* ls... */ }
exports.togglePref = () => { /* ls... */ }

How to add static files to an Electron app

How can I add JSON or TOML files in an Electron app for deployment? The following code works in development environment, but does not after packaging by electron-packager.
var presets = toml.parse(fs.readFileSync('presets.toml','utf8'));
According to the guide that took me way too long to find, the Electron team patched the fs module to provide a "virtual file system" under the root (/).
That means your file is accessible at fs.readFileSync('/presets.toml'); (notice the forward slash).
I went crazy till I found this one.
The problem is not adding it because it's already added.
Problem is finding base path after packaging.
So here it is:
const { app } = window.require('electron').remote;;
console.log(app.getAppPath());
Also, as you can see, if you're using React you need to use window.require instead of regular require (it throws nasty error otherwise).
Found about this here:
https://github.com/electron/electron/issues/3204#issuecomment-151000897

Resources