Get Default Editor from Extension or Content Type

swift
files_folders
finder

#1

This macro is the conclusion of my Swift experiments as posted in this thread. (Which started out as an alternate solution to @JMichaelTX’s perl stdin problem.)

Purpose

You have a file name extension or a Content Type (the latter is roughly the same as “UTI”, I think) and you want to know which program on your Mac is the default editor for that extension/UTI.

  • Examples for file name extensions: txt, doc, jpg, jpeg, scpt
  • Examples for Content Types (UTIs): public.text, com.adobe.pdf, org.openxmlformats.wordprocessingml.document

The macro will return the default editor as Bundle ID. If you enter an extension the script will also print the preferred UTI for that extension:

Bundle ID <-- UTI <-- extension

Usage

Run the macro and in the user prompt enter either an extension or an UTI. You can enter multiple extensions/UTIs, but don’t mix them:

Querying extensions

Querying UTIs

Installation

The macro is using two Swift scripts. To make them run faster I have pre-compiled them to executables; you find them on the DMG (well the stupid forum software doesn’t accept DMGs, so now it’s a DMG inside a ZIP):

Get_Default_Editor.dmg.zip (77.6 KB)

To let the macro find the executables just drop them anywhere in your PATH, and make sure KM knows about the PATH.

Install the macro itself (also on the DMG) by double-clicking it, as usual.

If you don’t want to install the executables in your PATH, then just drop them anywhere and add the explicit path in the two Shell Script actions. For example:

~/my/custom/bin/path/getDefaultEditorForContentType $KMVAR_Local__Extension_or_UTI

Alternatively…

…you can compile the scripts yourself with swiftc (or run them uncompiled). This is the source:

getDefaultEditorForContentType.swift

import Foundation
let arguments = Array(CommandLine.arguments.dropFirst())
for contentType in arguments {
    if let myApp = LSCopyDefaultRoleHandlerForContentType(contentType as CFString, LSRolesMask.editor) {
        let bundleID = Unmanaged.fromOpaque(myApp.toOpaque()).takeUnretainedValue() as CFString
        print ("\(bundleID) <-- \(contentType)")
    } else {
        print ("[No default handler]  <-- \(contentType)")
    }
}

getDefaultEditorForExtension.swift

import Foundation
let arguments = Array(CommandLine.arguments.dropFirst())
for arg in arguments {
	let ext = arg as CFString
	let contentType = UTTypeCreatePreferredIdentifierForTag(
			kUTTagClassFilenameExtension, ext, nil)?.takeRetainedValue()
	if let myApp = LSCopyDefaultRoleHandlerForContentType(contentType!, LSRolesMask.editor) {
			let bundleID = Unmanaged.fromOpaque(myApp.toOpaque()).takeUnretainedValue() as CFString
			print ("\(bundleID) <-- \(contentType!) <-- \(ext)")
	} else {
			print ("[No default handler]  <-- \(contentType!) <-- \(ext)")
	}
}

I’m a Swift learner, so I’m thankful for any hints how to streamline/improve the scripts :slight_smile:

I’m aware that there other means to get the default editor for a given extension, but it seems the Swift solution isn’t the worst one in that case.

BTW

Once the executables are in your PATH you can also use them from the Terminal.


Possible bug: no variable input for shell scripts
Feature Request: Bundle Format for KM Macros