How Do I get the Default App?


(Jim Underwood) #1

How Do I get the Default App?

I found this, which looks like a solution, but requires ASObjC:
Launch Services Concepts

In addition, some Launch Services functions apply not to specific individual items but to families of items defined by certain identifying characteristics. These characteristics can include:

A four-character file type code
A four-character creator signature
A filename extension
A MIME (Multipurpose Internet Mail Extension) type

For instance, the Launch Services function LSGetApplicationForInfo finds the preferred application for a family of documents defined by their file type, creator signature, filename extension, or any combination of these characteristics; the LSCopyApplicationForMIMEType function finds the preferred application for items with a specified MIME type.

My immediate need is for a “scpt” file. I want to open the default script editor, but I don’t actually have a file, just the text.

So, can anyone help me with the ASObjC to do this?




Well there are 3 great solutions in this thread. I picked the one by @ionah as the “official” solution since, IMO, it provides the best handler. My sincere thanks to all of you. This has been very helpful.

ASObjC solution by Shane

Shell Perl Solution by Chris

and finally

ASObjC Handler by @ionah Based on Shane’s Script

(Shane Stanley) #2

You can’t use Launch Services because it’s all C-based APIs, not Objective-C. For your particular problem, it’s simple enough to create a file, and then ask NSWorkspace. So:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set thePath to (POSIX path of (path to temporary items)) & "temp.scpt"
current application's NSFileManager's defaultManager()'s createFileAtPath:thePath |contents|:(missing value) attributes:(missing value)
set ws to current application's NSWorkspace's sharedWorkspace()
set theURL to ws's URLForApplicationToOpenURL:(current application's |NSURL|'s fileURLWithPath:thePath)
ws's openURL:theURL

(Christopher Stone) #3

Hey Shane,

Can you use URL-type or MIME type to similarly get the app?


(Christopher Stone) #4

Courtesy of Peter Lewis here’s how to do it with the shell and Perl:

# Auth: Christopher Stone
# dCre: 2017/11/26 02:35
# dMod: 2017/11/26 02:47 
# Appl: AppleScript
# Task: Return the Application name that supports a given URL-type.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Application, @Name, @URL-type

set theAppName to getDefaultAppNameForUrlType("applescript")

--> "Script Debugger"

on getDefaultAppNameForUrlType(urlType)
   set shCMD to "

/usr/bin/perl -MMac::InternetConfig -le 'print (GetICHelper \"" & urlType & "\")' \\
| sed -E 's!^.{4}!!'

   return do shell script shCMD
end getDefaultAppNameForUrlType


(Shane Stanley) #5

I’m guessing by “URL-type” you actually mean by UTI, and I believe the answer is no on both counts.

(Shane Stanley) #6

Doing it without writing to a file certainly feels cleaner.

(Jonas Whale) #7

I don’t get it. What is a URL type?

I prefer to rely on extensions, which any user has in memory.
That’s why the @ShaneStanley approach seems better to me.

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"

set appName to my getDefautltAppFor:"scpt" |as|:""
set appID to my getDefautltAppFor:"scptd" |as|:"id"
set appFile to my getDefautltAppFor:"applescript" |as|:"file"

on getDefautltAppFor:theExt |as|:theType
   set thePath to current application's NSTemporaryDirectory()'s stringByAppendingString:("temp." & theExt)
   current application's NSFileManager's defaultManager()'s createFileAtPath:thePath |contents|:(missing value) attributes:(missing value)
   set theWorkspace to current application's NSWorkspace's sharedWorkspace()
   set defaultAppURL to theWorkspace's URLForApplicationToOpenURL:(current application's |NSURL|'s fileURLWithPath:thePath)
   if defaultAppURL = missing value then return missing value -- or false
   if theType = "file" then
   	return defaultAppURL as «class furl»
   else if theType = "ID" then
   	return (current application's NSBundle's bundleWithURL:defaultAppURL)'s bundleIdentifier() as text
   	return defaultAppURL's URLByDeletingPathExtension()'s lastPathComponent() as text
   end if
end getDefautltAppFor:|as|:

(Jim Underwood) #8

Shane, too bad about Launch Services. But I appreciate the solution you provided.

I tend to agree.

Thanks Chris for a great solution.

(Shane Stanley) #9

Although @ccstone used the term URL type, I believe the code actually deals with extensions.

My only beef with it is that it’s so much slower, which is just me obsessing over speed.

(Christopher Stone) #10


Try “http” and “ftp”  ;-)


(Shane Stanley) #11

Oops. Your example used applescript, which can also be an extension, and I got my wires crossed.

(Jim Underwood) #12

You and Chris are both “speed demons”. LOL

For me, I’m only interested in material differences. And I don’t see any with the two scripts, run from Script Geek:

  1. ASObjC Method: 0.002 sec
  2. Shell Method: 0.066 sec

OK, so #1 is 30X faster, But I can’t really notice 0.066 sec on my iMac-27.
Chris will probably tell us he can notice the difference, so he’ll probably switch to Shane’s method. LOL

In my case I have a SSD, so the disk write is very fast. I wonder how much a spinning drive would slow it down?

Hey Shane, is there an easy way to get the app name from the nsURL?
(I can do the normal parsing, just thought there might be a filename property?)

(Shane Stanley) #13

You can get the lastPathComponent(), or to cope with the possibility of localization:

set {theResult, theName, theError} to theURL's getResourceValue:(reference) forKey:(current application's NSURLLocalizedNameKey) |error|:(reference)
theName as text

(Jim Underwood) #14

OK, the speed bug has bit me, so I’ll have to go for the solution that is 30X faster.

Here’s my rendition of Shane’s script as a handler, returning just the root name of the Default App:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set defaultApp to my getDefaultApp("scpt")
-->Script Debugger

on getDefaultApp(pExtStr)
  # Based on @ShaneStanley's script & enhancements
  #  2017-11-26
  set thePath to (POSIX path of (path to temporary items)) & "temp." & pExtStr
  set nsCurApp to current application
  --- Create a Temp File with Extension ---
  nsCurApp's NSFileManager's defaultManager()'s createFileAtPath:thePath |contents|:(missing value) attributes:(missing value)
  set ws to nsCurApp's NSWorkspace's sharedWorkspace()
  --- Get URL of Default App for That Extension ---
  set nsAppURL to ws's URLForApplicationToOpenURL:(nsCurApp's |NSURL|'s fileURLWithPath:thePath)
  --- Get the FileName of the App ---
  set {nsResult, nsAppName, nsError} to nsAppURL's getResourceValue:(reference) forKey:(nsCurApp's NSURLLocalizedNameKey) |error|:(reference)
  --- Get Just the Root Name of the App ---
  set appName to nsAppName as text
  set appName to text 1 thru ((offset of "." in (appName as text)) - 1) of appName
  return appName
end getDefaultApp

Thanks again, Shane. :+1:

(Jim Underwood) #15

Jonas, sorry I didn’t actually read your handler until just now. Since you said it was Shane’s approach, I just skipped it initially.

But I see now your handler is much better than mine.
Thanks for sharing. :+1:

(Jim Underwood) #16

Shane et al: I just wanted to mention that this script does not seem to need
use framework "AppKit"

It seem to run just fine without it.

(Shane Stanley) #17

All is not as it seems :slight_smile:

The NSWorkspace class is part of AppKit, so you should include the appropriate use framework statement. That doesn’t mean it won’t run without it — the framework’s info is probably already cached. But as a matter of reliability you should always include use statements for any frameworks you, well, use.

(Foundation and AppKit are going to be loaded by any host anyway, but the use statement is also key to AppleScript loading the framework’s .bridgesupport file, and without that some scripts simply won’t work. Better safe than sorry.)

(Shane Stanley) #18

Or perhaps more simply:

set appName to nsAppName's stringByDeletingPathExtension() as text

(Jim Underwood) #19

Shane, thanks for the clarification.

Is there any overhead, or penalty, for including a use framework statement that is not needed?

UPDATE: 2017-11-27 01:11 GMT-0600

FYI, I have tested the script without the use framework "AppKit" in these apps, and it runs find in all:

  • SD6
  • Script Editor
  • Script Geek
  • FastScripts
  • Keyboard Maestro

(Shane Stanley) #20

None that I’ve been able to measure.

(The proper “test” would be to quit the apps first, which I’m assuming you did.) In fact, this script doesn’t use anything that requires loading the .bridgesupport file, so it’s likely to be fine regardless. But that’s not exactly obvious to most scripters. And you leave yourself open to having problems if you edit it later.

And perhaps just as important for Script Debugger users, the presence or otherwise of a use framework statement is used to determine what terminology is offered in code-completion. So if you leave out AppKit, you won’t be offered NSWorkspace or any of its methods. And using code completion is not only a labor-saver (I shouldn’t have to tout the benefits of automation around here!), but it also offers a lot of hints to getting the code right.