I’ve posted this elsewhere, but it probably belongs here too.
One of the banes of scripting is how slow the Finder can sometimes be, and this is particularly so when you want the contents of a folder. For example, this:
tell application "Finder" to set theFiles to (sort (every file of <some folder>) by modification date)
can be quite slow if there are a lot of files involved. And if you use “entire contents” with a folder containing a lot of files, there’s a good chance it will whir away for two minutes, then time out. Fortunately ASObjC- based libraries mean you can side-step the Finder.
This handler is a bit more complicated, but most of the effort is in copying, pasting and saving in a library. It could do with someone defining some terminology to make it a bit easier to use, but it’s not exactly rocket science to use as-is.
The handler requires a POSIX path for the folder, and boolean values for whether you want to include folders as well as files, whether to also search subfolders, and whether to sort in ascending order. And it takes a key to sort on, which is where it’s a little more complicated.
For instructions on how to save an ASObjC-based library, see macscripter.net/viewtopic.php?id=41638. Here’s the handler:
use framework "Foundation"
on sortFolder:folderPosixPath byKey:theKey includeFolders:includeFolders entireContents:entireContents ascendingOrder:ascendingOrder
set keysToRequest to {current application's NSURLPathKey, current application's NSURLIsDirectoryKey, theKey} -- keys for values we want from each item
set theInfo to current application's NSMutableArray's array() -- array to store new values in
set theURL to current application's NSURL's fileURLWithPath:folderPosixPath -- make URL for folder because that's what's needed
set fileManager to current application's NSFileManager's defaultManager() -- get file manager
set theOptions to ((current application's NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((current application's NSDirectoryEnumerationSkipsHiddenFiles) as integer) -- options to use when enumerating directory
if not entireContents then -- extra option to skip deep search
set theOptions to theOptions + ((current application's NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
end if
-- enumerate the folder
set theEnumerator to fileManager's enumeratorAtURL:theURL includingPropertiesForKeys:keysToRequest options:theOptions errorHandler:(missing value)
repeat
set aURL to theEnumerator's nextObject() -- nextObject() returns the next URL held by the enumerator
if aURL is missing value then exit repeat -- missing value means there are no more, so exit
theInfo's addObject:(aURL's resourceValuesForKeys:keysToRequest |error|:(missing value)) -- get values and add to array
end repeat
if not includeFolders then -- filter out folders
set thePred to current application's NSPredicate's predicateWithFormat_("%K != YES", current application's NSURLIsDirectoryKey)
theInfo's filterUsingPredicate:thePred
end if
set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:theKey ascending:ascendingOrder -- describes the sort
theInfo's sortUsingDescriptors:{theDescriptor} -- do the sort
return (theInfo's valueForKey:(current application's NSURLPathKey)) as list -- extract paths and convert to list
end sortFolder:byKey:includeFolders:entireContents:ascendingOrder:
And you’d call it like this:
use theLib : script "<lib name>" -- name of the library you saved it in
use scripting additions
set theFiles to theLib's sortFolder:(POSIX path of (choose folder)) byKey:(current application's NSURLContentModificationDateKey) includeFolders:false entireContents:false ascendingOrder:true
If you wanted files instead of POSIX paths, you could add:
repeat with i from 1 to count of theFiles
set item i of theFiles to POSIX file (item i of theFiles)
end repeat
The tricky bit is knowing the key to use. The main ones are: NSURLAttributeModificationDateKey, NSURLContentAccessDateKey, NSURLContentModificationDateKey, NSURLCreationDateKey, NSURLLabelColorKey, NSURLLabelNumberKey, NSURLLocalizedLabelKey, NSURLLocalizedTypeDescriptionKey, NSURLNameKey, NSURLTypeIdentifierKey, NSURLFileSizeKey, NSURLFileAllocatedSizeKey, NSURLTotalFileAllocatedSizeKey, and NSURLTotalFileSizeKey.
Why bother? On my Mac, with about 400-odd items on my desktop, the difference in times is about 1.3 seconds vs 0.08 seconds. If I instead set entireContents to true and do the equivalent for the Finder, the ASObjC method blows out to about 0.2 seconds, while the Finder spends two minutes thinking and then times out.
For a hoot, I tried this:
set theFiles to theLib's sortFolder:(POSIX path of (path to home folder)) byKey:(current application's NSURLContentModificationDateKey) includeFolders:false entireContents:true ascendingOrder:true
It took a bit over a minute, although returning the list of more than 90,000 items took quite a bit longer. I wouldn’t want to try it with Finder…