close
close

rust – Save MacOS.app file as default file extension when opened, pass clicked file as argument

rust – Save MacOS.app file as default file extension when opened, pass clicked file as argument

I have an application called FileOpener.app, it has the following content:

FileOpener.app
  - Contents
    Info.plist
    - MacOS
      - FileOpener
    - Resources

The contents of Info.plist are:

<?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>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>FileOpener</string>
    <key>CFBundleIdentifier</key>
    <string>com.example.FileOpener</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>FileOpener</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>

    <!-- Document Types -->
    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeName</key>
            <string>Test File</string>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>LSHandlerRank</key>
            <string>Owner</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.example.testfile</string>
            </array>
        </dict>
    </array>

    <key>UTExportedTypeDeclarations</key>
    <array>
        <dict>
            <key>UTTypeIdentifier</key>
            <string>com.example.testfile</string>
            <key>UTTypeDescription</key>
            <string>Test File</string>
            <key>UTTypeConformsTo</key>
            <array>
                <string>public.data</string>
            </array>
            <key>UTTypeTagSpecification</key>
            <dict>
                <key>public.filename-extension</key>
                <array>
                    <string>testfile</string>
                </array>
                <key>public.mime-type</key>
                <string>application/x-testfile</string>
            </dict>
        </dict>
    </array>


    <key>NSDocumentsFolderUsageDescription</key>
    <string>Description goes here</string>

    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>file</string>
    </array>
</dict>
</plist>

I have a super simple rust code:

fn log_message(message: &str) {
    let mut log_file = OpenOptions::new()
        .create(true)
        .append(true)
        .open("/tmp/fileopener.log")
        .unwrap();
    writeln!(log_file, "{}", message).unwrap();
}

fn main() -> notify::Result<()> {
    let args: Vec<String> = env::args().collect();
    log_message(&format!("Received arguments: {:?}", args));

    if args.len() != 2 {
        eprintln!("Usage: {} <path to .testfile file>", args(0));
        log_message("Incorrect number of arguments.");
        return Ok(());
    }
}

So every time the binary is called, it saves the arguments to a tmp file, expecting a secondary argument of a file path to be passed to it.

If I run ./FileOpener/Contents/MacOS/FileOpener ./path/to/file.testfile

The arguments are passed correctly.

If I try to drag file.testfile onto FileOpener.app, the path to file.testfile is not passed as an argument, the only argument is the path to FileOpener.app. If I try to double-click on file.testfile, FileOpener.app OPENS, but the path to file.testfile is not passed as an argument, the only argument is the path to FileOpener.app.

What I’m trying to do is register the .testfile extension so that when double-clicked it will open FileOpener.app and pass the path of the clicked (or dragged) file as the second argument.

I also tried code signing, sudo codesign --deep --force --sign - ./FileOpener.appby copying it to /Applications, enabling full disk access via security, etc. The secondary argument I need, i.e. the path to the file that was clicked, is never passed.