A tiny macOS CLI that sets the default app for a file type — the thing System Settings → General → Login Items & Extensions → Default web browser does, but for any file extension or Uniform Type Identifier (UTI), and scriptable.
Useful when you want .csv (or .log, .env, whatever) to always open in a
specific app without hunting through Finder's "Get Info" → "Open with" →
"Change All..." dialog by hand.
macOS decides which app opens a file type via LaunchServices, using a
UTI (Uniform Type Identifier) — a reverse-DNS-style string like
public.plain-text or com.microsoft.excel.csv — rather than the file
extension directly. sethandler resolves the extension you give it to a UTI,
then calls the same LaunchServices API (LSSetDefaultRoleHandlerForContentType)
that Finder uses, registering your chosen app's bundle ID as the default
handler for that type.
This changes a systemwide, persistent setting (stored by LaunchServices, not by this tool) — the same thing Finder's "Change All..." does. It affects every file of that type for your macOS user account, not just files opened through this CLI.
- macOS
- The target app must already be installed (LaunchServices needs to know its bundle ID exists — see Finding a bundle ID below)
swiftc sethandler.swift -o sethandlerNo dependencies beyond the Swift toolchain that ships with Xcode / Xcode
Command Line Tools (xcode-select --install if swiftc isn't found).
sethandler <bundle_id> <extension_or_uti> [more extensions/UTIs...]Arguments starting with . are treated as file extensions and resolved to a
UTI automatically. Anything else is assumed to already be a UTI.
Make an app the default handler for .csv and .tsv files:
sethandler com.devshed.coder .csv .tsv
✅ Set handler for UTI: public.comma-separated-values-text
✅ Set handler for UTI: public.tab-separated-values-textSet a default handler across several extensions at once:
sethandler com.microsoft.VSCode .js .ts .tsx .jsonTarget a UTI directly instead of an extension:
sethandler com.devshed.coder public.comma-separated-values-text✅ Set handler for UTI: public.comma-separated-values-text
❌ Failed for UTI: .foobar (-54)
A ❌ usually means the extension didn't resolve to a known UTI, or the bundle ID isn't registered with LaunchServices (the app has never been launched, or isn't installed).
osascript -e 'id of app "Coder"'or read it directly from the app bundle:
defaults read /Applications/Coder.app/Contents/Info.plist CFBundleIdentifierRight-click any file of that type in Finder → Get Info → "Open with:" should
now show your app selected. Alternatively, if you have duti installed
(brew install duti):
duti -x csv- Changes are systemwide for your user account — there's no per-tool or per-folder scoping.
- Some apps re-register themselves as the default handler for their own file
types on launch (common with editors/IDEs). If your setting reverts, that's
the other app reclaiming it, not a bug in
sethandler— rerun it after opening the other app, or check that app's settings for an "always reopen as default" preference. - Requires the target extension to resolve to a UTI that Apple's system or
an installed app has already declared. Fully custom/unregistered
extensions may need the owning app to declare a
UTExportedTypeDeclarationsentry in its own Info.plist beforesethandlercan target them.
MIT © DevShed Labs — see LICENSE.