B4J Question MacSigner - File path as argument when double clicking an associated file on Mac

gregchao

Active Member
Licensed User
Longtime User
I have successfully used MacSigner to create an application on Mac. On my Windows version, I set associations to an extension (e.g. .PBN) and when I double click on the file, the path is automatically sent as an argument that I can get through Main.appnames. On the Mac, however, this is not the case.

Reading up on the matter, I have read an article that says, "To have a macOS application receive a file path as an argument when you double-click a file (or drag-and-drop it), you must register that app to handle specific file types and utilize Apple’s Launch Services, which sends the file path as an Apple Event. If you are developing your own .app bundle, you must inform macOS that your app can handle files.
  • Edit Info.plist: Add the CFBundleDocumentTypes key to define which file extensions or UTIs (Uniform Type Identifiers) your app supports.
  • Handle Apple Events: Your application code must handle the appleevent aevt/odoc (Open Documents) to receive the file paths.
  • SwiftUI/AppKit: Implement the application(_penFiles delegate method. [1, 2]
I was wondering since the MacSigner creates the info.plist, what code would need to be modified on the MacSigner. And then, what code would need to be changed or modified in B4J code to receive the appleevent aevt/odoc? Does the Main.appnames work automatically?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
After digging deep into this feature and almost getting it to work properly in relevant cases, I see that it fails because of a JavaFX bug: https://bugs.openjdk.org/browse/JDK-8239590

The current situation is that it is possible to handle double clicks on files while the app is running, however if the app isn't running, the app will start and the "file open event" will be lost.
 
Upvote 0

gregchao

Active Member
Licensed User
Longtime User
Erel, thanks for trying. I found a work-around. It is more complex. I was hoping you would be successful before I fully implement this approach.

Step 1. Set up a helper application (e.g. xxxOpener) using Mac “Automator.app” that will run the bash script
Bash:
open -a xxx –args pathoffile
where xxx is your application and pathoffile is the path of your double-clicked file. The path will be passed into B4J variable ‘main.appnames’

Step 2. Set file association to your application using a bash program called “duti”. It requires your application's "id" that can be found using command ‘mdls’.
Bash:
Appid = $(mdls -name kMDItemCFBundleIdentifier -r pathtofile/xxx.app)
./duti -s $appid .pbn all   #.pbn is an example extension

Step 3. Automate this process by incorporating duti commands into a script file and zipping it with duti.exe. Save it to File.DirAssets. When running your B4j program for the first time, unzip these files and run the script file to set associations using "shell" command in B4j.

Step 4. After MacSigner, run Packages (use "Raw Package" template) and put both your application (xxx) and the your helper application (xxxOpener) in the Application folder (using PayLoad Tab). This will create a single installation file (.pkg) for both your applications.

Here are some details for step 1:
1. Open Automator (in Applications folder) and select New Document > Application.
2. Search for the "Run Shell Script" action and drag it into the workflow area.
3. Set the following options:
  • Shell: /bin/bash (or /bin/zsh)
  • Pass input: "to as arguments".
  • Insert following code.
Bash:
for f in "$@"
do
    # $f contains the full POSIX file path
    open -a yourapplicationhere –args “$f”
done

Here are some details for Step 2
  • Duti will allow you to set associations on a Mac using terminal. On Mac, load duti using Homebrew. After installing Homebrew, type "brew install duti" in terminal
  • Find location of installed bin file. Alias is typically at /usr/local/bin/duti. From Alias, find true location of .exe.
  • Note - To see hidden files on a Mac, pressing Command + Shift + “.”
  • Actual path will be something like: /usr/local/Cellar/duti/1.5.4_1/bin/duti
  • Move duti file to unrestricted location like “/documents”.
  • If you want to test duti in terminal, here are some test commands:
Bash:
mdls -name kMDItemCFBundleIdentifier -r pathofapplication/xxs.app  #to get appid; e.g. “fdp.xxx.macrelease”
./duti -s fdp.xxx.macrelease .pbn all  #to set associations
./duti -x pbn  #to check if association is set
  • To Remove association, go to pbn file. Right click>Get Info>Open with>(Change it to something else)>Change All
  • Notes - ./ required when running unix executable files in terminal.
Here is my sample script. Saved as “myscript.sh.”
Bash:
#!/bin/bash
appname=/Applications/$1.app    #$1 is first argument with calling script
if [ -d $appname ]; then  #check if appname exists in computer wide application folder
    name=$(mdls -name kMDItemCFBundleIdentifier -r $appname)  #to get app identifier name
else
    appname1=~/Applications/$1.app   #~indicates local
    name=$(mdls -name kMDItemCFBundleIdentifier -r $appname1)  #check local application folder
fi
chmod +x "duti"  #for permissions
./duti -s $name .pbn all  #to set associations
./duti -x pbn  #to check if association is set

Here is some details on Step 3.
After unzipping in B4j, run the script as:
B4X:
Dim shlDD As Shell
shlDD.Initialize("shlDD", "bash",  Array As String("myscript.sh", "xxxOpener"))
shlDD.WorkingDirectory = DutiFullPath
shlDD.RunWithOutputEvents(10000) 'set a timeout
 
Upvote 0
Cookies are required to use this site. You must accept them to continue using the site. Learn more…