Your solution probably works, but perhaps what I came up with might be more workable.
Of the proposed 3 options you came up with, indeed the LSSharedFile*
commands are the best solution.
With the advent of AppleScript script bundles (.scptd) and scripts that can be saved as application bundles (.app), you can quite easily include custom executables inside the bundle. As a result, you can make use of these executables to accomplish what you need to do if you couldn't accomplish it with AppleScript alone or the default BSD subsystem tools.
So, in Xcode I created a new "Foundation Tool" project which used the following code:
int main(int argc, const char * argv[]) {
@autoreleasepool {
UInt32 resolveFlags = 0;
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
if (arguments.count > 1) {
NSArray *revisedArguments = [arguments
subarrayWithRange:NSMakeRange(1, arguments.count - 1)];
for (NSString *arg in revisedArguments) {
if ([arg isEqualToString:@"--noUserInteraction"]) {
resolveFlags |= kLSSharedFileListNoUserInteraction;
} else if ([arg isEqualToString:@"--doNotMountVolumes"]) {
resolveFlags |= kLSSharedFileListDoNotMountVolumes;
}
}
}
LSSharedFileListRef sharedFileListRef = LSSharedFileListCreate(NULL,
kLSSharedFileListRecentDocumentItems, NULL);
NSArray *sharedFileListItemRefs =
(NSArray *)LSSharedFileListCopySnapshot(sharedFileListRef, NULL);
for (id itemRef in sharedFileListItemRefs) {
NSURL *resolvedURL = nil;
LSSharedFileListItemResolve((LSSharedFileListItemRef)itemRef,
resolveFlags, (CFURLRef *)&resolvedURL, NULL);
if (resolvedURL) {
printf("%s\n", resolvedURL.path.fileSystemRepresentation);
[resolvedURL release];
}
}
if (sharedFileListRef) CFRelease(sharedFileListRef);
[sharedFileListItemRefs release];
return EXIT_SUCCESS;
}
}
While this code is similar to yours, instead of having to write the results to an intermediary file, it simply prints the file paths to standard out. This should allow dramatically-simplified coding on the AppleScript end of things. The result of building this Xcode project is a single "Unix Executable File" named recentItemsFinagler
instead of an application bundle.
To make use of this built executable in an AppleScript, you first need to make sure the script is saved as a Script Bundle or Application and then the Bundle Contents
toolbar item should be enabled like in the image below:
Clicking that toolbar item shows the drawer which shows the contents of the script bundle or application bundle. To add your custom executable, just drag and drop it from the Finder like in the image below:
This will copy it into the script bundle like shown:
To locate this executable at runtime, you can use the path to resource
AppleScript command which is part of the StandardAdditions.osax
. (Note that depending on how you try to make use of the path to resource
command in your script, you may encounter "Resource not found" errors when you run the script from within AppleScript Editor as opposed to running it by double-clicking on it in the Finder. If you encounter this type of an error, first make sure you've saved all of your changes to the script, then quit out of AppleScript Editor, then launch the script application by double-clicking on it in the Finder, after the script finishes running, re-open AppleScript Editor, then re-open the script application, then try running from within AppleScript Editor and see if it works).
So, to make use of this recentItemsFinagler
in your AppleScript, you could do something like the following:
property recentItemsFinagler : missing value
if (recentItemsFinagler = missing value) then
set recentItemsFinagler to (path to resource "recentItemsFinagler")
end if
set toolPath to quoted form of POSIX path of recentItemsFinagler
set recentItemsString to (do shell script toolPath & " --noUserInteraction --doNotMountVolumes")
set recentItemsPaths to paragraphs of recentItemsString
I'll go through this AppleScript line by line to explain what it's doing. We first create a global property recentItemsFinagler
, and set its initial value to missing value
. (missing value
is the AppleScript equivalent to Objective-C's nil
).
In case you're not aware of it, global properties in AppleScript are like global variables in other languages with one important addition: when you run the script and assign a value to the property, that value is actually saved in the script file itself, and will persist to the next time you run the script.
The script first checks to see if recentItemsFinagler
is equal to missing value
, and if it is, sets it to the result of (path to resource "recentItemsFinagler")
, which is an AppleScript alias
reference. This is similar to an alias in the Finder in that it is able to successfully track changes like renames, and moving from one folder to another. If I had instead stored it as a simple string, and then moved this AppleScript application bundle from my Documents folder to my Desktop, the path would no longer be valid, and the property would have to be updated each time the script was run.
Anyway, we then set recentItemsString
to the result of the AppleScript do shell script
recentItemsFinagler
tool, which is a string. To turn this into an AppleScript list of strings which represent paths, you use the paragraphs of
command.
So by including the executable inside the script or AppleScript application's bundle, you can get the desired result in less than 10 lines of code.
Sample project: RecentItemsFinagler.zip