Infos about the recent documents are stored in : (path to application support from user domain as text) & “com.apple.sharedfilelist:com.apple.LSSharedFileList.RecentDocuments.sfl2”
Infos about the recent applications are stored in : (path to application support from user domain as text) & “com.apple.sharedfilelist:com.apple.LSSharedFileList.RecentApplications.sfl2”
How may we extract the names and/or the paths of the described recent items.
I found pieces of code which did that with old ways of storing these infos but none of them apply to the “new” one.
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 9 septembre 2019 13:58:02
Those files are binary property lists archived with NSKeyedArchiver. The URLs are security scoped bookmarks which can be resolved with NSURL API
This script extracts the URLs as string paths into a variable documentPaths
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
property |⌘| : a reference to current application
set recentDocumentsPath to POSIX path of (path to application support from user domain) & "com.apple.sharedfilelist/com.apple.LSSharedFileList.RecentDocuments.sfl2"
set plistData to |⌘|'s NSData's dataWithContentsOfFile:recentDocumentsPath
set recentDocuments to |⌘|'s NSKeyedUnarchiver's unarchiveObjectWithData:plistData
set documentPaths to {}
repeat with aDocument in (recentDocuments's objectForKey:"items")
set documentBookmark to (aDocument's objectForKey:"Bookmark")
set {documentURL, resolveError} to (|⌘|'s NSURL's URLByResolvingBookmarkData:documentBookmark options:0 relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(reference))
if resolveError is missing value then
set end of documentPaths to documentURL's |path|()
else
display dialog resolveError's localizedDescription as text
end if
end repeat
I added to your script’s end documentPaths to return it, and this is list of NSString paths in Script Debugger, and list of IDs in Script Editor. It is little strange for me.
So, to get as strings list always:
set end of documentPaths to documentURL's |path|() as string
I added some instructions allowing us to define which recents items we want to get.
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
property |⌘| : a reference to current application
tell application "Finder"
set cancelBtn to localized string "AL1" --> "Annuler"
set OKBtn to localized string "AL4" --> "OK"
end tell
set begOfPath to (path to application support from user domain as text) & "com.apple.sharedfilelist:"
set item1 to "Documents"
set item2 to "Applications"
set item3 to "Choose an application"
set whatToDo to choose from list {item1, item2, item3} default items {item3}
if whatToDo is false then error number -128
set whatToDo to whatToDo's item 1
if whatToDo is item3 then
set appName to name of (choose application)
set appleApp to false
if appName is in {"Keynote", "Numbers", "Pages"} then
set appName to "Iwork." & appName
set appleApp to true
else if appName is "Script Editor" then
set appName to "scripteditor2"
set appleApp to true
else if appName is "Imovie" then
set appName to "imovieapp"
set appleApp to true
else if appName is "Xcode" then
set appName to "dt.xcode"
set appleApp to true
else if appName is "QuickTime Player" then
set appName to "quicktimeplayerx"
set appleApp to true
else if appName is "System Profiler???" then
set appName to "systemprofiler"
set appleApp to true
else if appName is "BBEdit" then
set appName to "com.barebones.bbedit"
else if appName is "Libre Office" then
set appName to "org.libreoffice.script"
else if appName is "Script Debugger" then
set appName to "com.latenightsw.scriptdebugger7"
else if 5 = 6 then
--
end if
if appleApp then
set recentDocumentsPath to POSIX path of (begOfPath & "com.apple.LSSharedFileList.ApplicationRecentDocuments:com.apple." & appName & ".sfl2")
else
set recentDocumentsPath to POSIX path of (begOfPath & "com.apple.LSSharedFileList.ApplicationRecentDocuments:" & appName & ".sfl2")
end if
else
set recentDocumentsPath to POSIX path of (begOfPath & "com.apple.LSSharedFileList.Recent" & whatToDo & ".sfl2")
end if
try
set plistData to |⌘|'s NSData's dataWithContentsOfFile:recentDocumentsPath
set recentDocuments to |⌘|'s NSKeyedUnarchiver's unarchiveObjectWithData:plistData
set documentPaths to {}
repeat with aDocument in (recentDocuments's objectForKey:"items")
set documentBookmark to (aDocument's objectForKey:"Bookmark")
set {documentURL, resolveError} to (|⌘|'s NSURL's URLByResolvingBookmarkData:documentBookmark options:0 relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(reference))
if resolveError is missing value then
set end of documentPaths to documentURL's |path|() as string
--else
--display dialog resolveError's localizedDescription as text
end if
end repeat
documentPaths as list
on error
error "The selected application is not treated or has no sfl2 associated file"
end try
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 9 septembre 2019 20:48:42
Added instructions required to treat particular cases of Apple applications and code required to treat three non apple applications.
Complete to fit your needs after looking at the contents of the folder :
[format]((path to application support from user domain as text) & “com.apple.sharedfilelist:com.apple.LSSharedFileList.ApplicationRecentDocuments:”)[/format]
set appName to display dialog "Define an application" default answer "Iwork.numbers" buttons {cancelBtn, OKBtn} default button OKBtn cancel button cancelBtn
with this:
set appName to name of (choose application)
But I can’t understand. It seems that separately for applications the code does not work anyway?
Update: I apologize - your code works just fine if the application has the recent documents at the moment. If they are not already, an error is thrown. That is, you need to put the last part of the code in a try block. Or, in the if block:
if recentDocuments ≠ missing value then
repeat with aDocument in (recentDocuments's objectForKey:"items")
set documentBookmark to (aDocument's objectForKey:"Bookmark")
set {documentURL, resolveError} to ¬
(|⌘|'s NSURL's URLByResolvingBookmarkData:documentBookmark options:0 ¬
relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(reference))
if resolveError is missing value then
set end of documentPaths to documentURL's |path|() as string
--else
--display dialog resolveError's localizedDescription as text
end if
end repeat
end if
If you select Numbers, you will get the name “Numbers” while the system requires “Iwork.numbers”
There is a workaround:
set appName to name of (choose application)
if appName is in {"Keynote", "Numbers", "Pages"} then set appName to "Iwork." & appName
if appName is "Imovie" then set appName to "imovieapp"
# Complete with non-Apple applications
Of course don’t ask me for an explanation of this non-standard behavior.
Maybe it would be useful to add “IBooks” to the list of applications requiring to be “corrected”.
PS: Since 1943/12/31, my first name is Yvan and I’m accustomed to it. Please, don’t torture it!
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 9 septembre 2019 21:22:36
This workaround is better for me, as I don’t like manual operations. So, thanks for your script. It goes to my Scripts library in this form:
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
property |⌘| : a reference to current application
set {item1, item2, item3} to {"Documents", "Applications", "Choose an application"}
set whatToDo to choose from list {item1, item2, item3} default items {item3}
if whatToDo is false then error number -128
set whatToDo to whatToDo's item 1
if whatToDo is item3 then
set appName to name of (choose application)
if appName is in {"Keynote", "Numbers", "Pages"} then set appName to "Iwork." & appName
set recentDocumentsPath to POSIX path of ((path to application support from user domain as text) & "com.apple.sharedfilelist:com.apple.LSSharedFileList.ApplicationRecentDocuments:com.apple." & ¬
appName & ".sfl2")
else
set recentDocumentsPath to POSIX path of ((path to application support from user domain as text) & "com.apple.sharedfilelist:com.apple.LSSharedFileList.Recent" & whatToDo & ".sfl2")
end if
set plistData to |⌘|'s NSData's dataWithContentsOfFile:recentDocumentsPath
set recentDocuments to |⌘|'s NSKeyedUnarchiver's unarchiveObjectWithData:plistData
set documentPaths to {}
if recentDocuments ≠ missing value then
repeat with aDocument in (recentDocuments's objectForKey:"items")
set documentBookmark to (aDocument's objectForKey:"Bookmark")
set {documentURL, resolveError} to (|⌘|'s NSURL's URLByResolvingBookmarkData:documentBookmark ¬
options:0 relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(reference))
if resolveError is missing value then
set end of documentPaths to documentURL's |path|() as string
else
display dialog resolveError's localizedDescription as text
end if
end repeat
end if
documentPaths
I edited the script in message #4 so that it treat some non-standard Apple naming and show how to complete with non Apple products (I coded three examples).
I would be glad to know why many applications aren’t proposed by choose application.
It’s the case for many applications which I bought from the App Store and which are stored in a subfolder named “Applications_MAS” . Xcode or Stuffit Expander are proposed but EasyDraw or EasyFind aren’t.
Same behavior which many applications stored in a subfolder named “Applications perso”. BBEdit or Script Debugger are proposed but Libre Office isn’t.
Some applications stored in the Applications folder are missing too: Data Rescue.app, DiskMaker X 7 for High Sierra.app, GIMP-2.10.app, Malwarebytes.app are some of them
For info, here is a list of the sfl2 files dedicated to applications which are available on my SSD:
The list above without extensions is list of CFBundleIdentifier. I don’t know how, but is seems, this maybe used for automated getting required by system names.
How can I convert application name to its CFBundleIdentifier?
set appName to name of (choose application)
set appName to id of application appName
I applyed this and… Hm, it works:
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
property |⌘| : a reference to current application
set {item1, item2, item3} to {"Documents", "Applications", "Choose an application"}
set whatToDo to choose from list {item1, item2, item3} default items {item3}
if whatToDo is false then error number -128
set whatToDo to whatToDo's item 1
if whatToDo is item3 then
set appName to name of (choose application)
set appName to id of application appName
set recentDocumentsPath to POSIX path of ((path to application support from user domain as text) & "com.apple.sharedfilelist:com.apple.LSSharedFileList.ApplicationRecentDocuments:" & ¬
appName & ".sfl2")
else
set recentDocumentsPath to POSIX path of ((path to application support from user domain as text) & "com.apple.sharedfilelist:com.apple.LSSharedFileList.Recent" & whatToDo & ".sfl2")
end if
set plistData to |⌘|'s NSData's dataWithContentsOfFile:recentDocumentsPath
set recentDocuments to |⌘|'s NSKeyedUnarchiver's unarchiveObjectWithData:plistData
set documentPaths to {}
if recentDocuments ≠ missing value then
repeat with aDocument in (recentDocuments's objectForKey:"items")
set documentBookmark to (aDocument's objectForKey:"Bookmark")
set {documentURL, resolveError} to (|⌘|'s NSURL's URLByResolvingBookmarkData:documentBookmark ¬
options:0 relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(reference))
if resolveError is missing value then
set end of documentPaths to documentURL's |path|() as string
else
display dialog resolveError's localizedDescription as text
end if
end repeat
end if
documentPaths
Here is a new version which trigger only existing files.
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
property |⌘| : a reference to current application
Germaine()
on Germaine()
set begOfPath to (path to application support from user domain as text) & "com.apple.sharedfilelist:"
set begOfPathLong to begOfPath & "com.apple.LSSharedFileList.ApplicationRecentDocuments:"
set specialCases to {"Applications", "Documents"}
tell application "Finder"
set theChoices to specialCases & name of files of folder begOfPathLong
end tell
set appName to choose from list theChoices
if appName is false then error number -128
set appName to appName's item 1
if appName is in specialCases then
set recentDocumentsPath to POSIX path of (begOfPath & "com.apple.LSSharedFileList.Recent" & appName & ".sfl2")
else
set recentDocumentsPath to POSIX path of (begOfPathLong & appName)
end if
try
set plistData to |⌘|'s NSData's dataWithContentsOfFile:recentDocumentsPath
set recentDocuments to |⌘|'s NSKeyedUnarchiver's unarchiveObjectWithData:plistData
set documentPaths to {}
repeat with aDocument in (recentDocuments's objectForKey:"items")
set documentBookmark to (aDocument's objectForKey:"Bookmark")
set {documentURL, resolveError} to (|⌘|'s NSURL's URLByResolvingBookmarkData:documentBookmark options:0 relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(reference))
if resolveError is missing value then
set end of documentPaths to documentURL's |path|() as string
end if
end repeat
documentPaths as list
on error
error "The selected application is not treated or has no sfl2 associated file"
end try
end Germaine
I built another version in which I use a list of apps derived manually from the list of available files.
I feel that a version which doesn’t require some duty from the user is better.
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 10 septembre 2019 09:07:24
Thanks, Yvan. I tried your last version too. I liked it. Now, with our last posts your code has not 1 but 2 extremely cool solutions. Thanks again to Stefan for starting point.
As I am pig headed, I repeat what I already wrote:
I would be glad to know why many applications aren’t proposed by choose application.
It’s the case for many applications which I bought from the App Store and which are stored in a subfolder named “Applications_MAS” . Xcode or Stuffit Expander are proposed but EasyDraw or EasyFind aren’t.
Same behavior which many applications stored in a subfolder named “Applications perso”. BBEdit or Script Debugger are proposed but Libre Office isn’t.
Some applications stored in the Applications folder are missing too: Data Rescue.app, DiskMaker X 7 for High Sierra.app, GIMP-2.10.app, Malwarebytes.app are some of them
And I add a question: where is iBooks storing its list of recents items?
It has its own Recent items menu but I see no file storing such infos and the Recents Items > Documents menu doesn’t display books open in iBooks.
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 10 septembre 2019 13:57:58
“The choose application dialog initially presents a list of all applications registered with the system. To choose an application not in that list, use the Browse button, which allows the user to choose an application anywhere in the file system.”
As I understand, on Mac system registered apps is those from identified by Apple developers… It is intresting,were on Mac is registered apps path… It should be managed by Gatekeeper
So, my answer is: no, all applications are proposed by choose application, with Browse button in this dialog.
I suspect that’s well out of date — the dialog changed quite a long time ago (when script apps moved to bundles, I suspect — it was the move to bundles with Info.plist scriptability entries that made it feasible to easily check which are scriptable).
Go into Script Editor and choose File → Open Dictionary. The list will obviously only show scriptable apps and libraries. Click on the Kind column so applications are sorted before script libraries.
Leave the dialog open and go into Script Debugger and run this script:
choose application
Compare the lists in the two dialogs. I think you’ll find they’re identical.
You are right. I ran your scripts, Shane. The dialogs are almost same for Open dictionary and for Choose application.
But this may again be a coincidence, based on Apple’s policy, to highlight the programs of those developers who pay. In any case, the user can select any application in the choose application through the “Browse” button, and this is important for this topic.
And, is one important difference: press Browse in Open dictionary, go to Applications folder. You can’t choose nonscriptable app. But doing this with Browse in Choose application dialog you can choose any application. So, I think, all is related to money and not to scriptability.
I don’t know, Shane, may be one scriptable app non registered to work on Mac?
Thank you Shane.
It makes sense.
I don’t believe that it’s a problem of money because most of the applications bought thru MacAppStore aren’t proposed although they came from registered developers. The proposed ones are : Amphetamine, Aperture, JSON Helper, Omnigrafle, SiteSucker, Stuffit Expander, The Unarchiver, XCode, Instruments and Simulator which belong to Xcode and iBooks Author. All of them are scriptable.
Being forced to click Browse is really annoying so I will stay with the script which offer only the applications which have a related sfl file.
So now the remaining question is : where is iBook storing it’s list of recent items?
It’s curious that an application made by Apple doesn’t apply the standard scheme.
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 10 septembre 2019 16:47:05
You’re a hard man to convince. Save an applet in /Applications. See that it doesn’t show up in choose application. Now open its Info.plist file and add:
<key>NSAppleScriptEnabled</key>
<true/>
Open the script in an editor, make a change and save. Now try choose application again.
Yes it’s true. But you convinced me with the last test. It is very rare for a knowledgeable person to share his knowledge so easily. Thank you, Shane, for the great help on this site. I always read your posts with interest.