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
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
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
else
return defaultAppURL's URLByDeletingPathExtension()'s lastPathComponent() as text
end if
end getDefautltAppFor:|as|:
For me, I’m only interested in material differences. And I don’t see any with the two scripts, run from Script Geek:
ASObjC Method: 0.002 sec
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?)
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
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
# http://forum.latenightsw.com/t/how-do-i-get-the-default-app/830/2
# 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
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.)
(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.