I am trying to get list of urls for open documents to use in a swift app. I have the following script.
tell application "System Events"
set visibleProcesses to name of every application process whose visible is true and name is not "Finder"
set appList to {}
repeat with appName in visibleProcesses
set end of appList to appName
end repeat
log appList
set openDocs to {}
repeat with appName in appList
set strAppName to (appName as string)
tell application strAppName
try
repeat with aDoc in documents
set end of openDocs to path of aDoc
end repeat
on error errMsg
log "ERROR: " & errMsg
end try
end tell
end repeat
end tell
However when I try to get the list of urls from the variable of running apps, I get the following error: (*ERROR: System Events got an error: Can’t get application "Preview".*)
however if I hardcode the name in: tell application "Preview"
It works as expected.
So I am sure I am referencing the app in a wrong way.
Thanks
Reza
tell application "System Events"
set appList to name of every application process whose visible is true and name is not "Finder"
end tell
set tid to text item delimiters
set text item delimiters to return
set openDocs to {}
repeat with appName in appList
set appName to contents of appName
set ascript to {"set openDocs to {}", "tell application \"" & appName & "\"", "try", "repeat with aDoc in documents", "set end of openDocs to path of aDoc", "end repeat", "on error errMsg", "log \"ERROR: \" & errMsg", "end try", "end tell", "return openDocs"}
set ascript to ascript as text
set moreDocs to (run script ascript)
repeat with aDoc in moreDocs
set aDoc to contents of aDoc
if aDoc ≠ missing value then set end of openDocs to aDoc
end repeat
end repeat
set text item delimiters to tid
get openDocs
As you probably know, this script, when run, will (typically) execute successfully for applications that are (Apple-)scriptable, for which documents will return a reference to a list containing the collection of its document class objects, if any.
Rather than looping through the collection simply to retrieve the path property for each document object separately—which necessitates the dereferencing of the collection (partially) and each item (fully) to do this—you can instead enumerate the path property over the entire collection as a single reference:
set ascript to "
try
tell application \"appName\" to get ¬
the path of every document
on error
{}
end try
"
There’s probably not much value in returning an error message, as all scriptable applications should have the document class definition as part of the core terminology, even if the application never makes any use of it. So even if no document class objects exist, properties such as path that are enumerated by reference will always return a valid result.
Therefore, errors that are caught by the try block will mean one of three things:
the document reference is invalid, which means the application isn’t scriptable; or
a valid document reference either yields an empty collection or one with objects that do not have a path property defined; or
the application reference is invalid, which indicates that appName is not a system-registered name of an application. Since appName gets its value from System Events, it will (almost) always be a valid name, and will therefore be able to instantiate a valid AppleScript application object instance. Exceptions do exist (Microsoft VS Code comes to mind) as Apple doesn’t impose strict requirements on application names. If you wanted more robustness, yo can enumerate System Events’s process class objects either by using the bundle identifier property, or by using the file or application file properties, both of which have path and POSIX path properties. Any of these alternatives will guarantee a valid AppleScript application object reference, instantiated with a bundle identifier value by way of the application id <bundle identifier> key form, or with a path or POSIX path value in the same way as with an application’s name, i.e. application "<POSIX path>".
On a related note, consideration can be given to enumerating the processes with System Events even more effectively, particularly as we already know that your approach to identifying open documents relies upon the scriptability of an application in order to enumerate its documents object collection.
Non-scriptable applications cannot be queried with AppleScript in this manner, allowing us to remove the most significant culprit for errors that are thrown (and caught) when ascript is executed. Moreover, eenumerating only scriptable processes and omitting the rest that we know we cannot use (the non-scriptable ones) will confer improved efficiency and execution speed.
For this, the has scripting terminology property of process class objects allows us to determine the scriptability of an application. We could therefore consider replacing this:
with this:
tell application "System Events" to set appPaths ¬
to the application file's POSIX path of ¬
processes whose background only is false ¬
and has scripting terminology is true
Addendum
On experimentation, it seems executing an AppleScript string (ascript) is not necessary. Although not fully tested, the following script seems, from preliminary testing, to collate a list of open file paths:
set docpaths to {}
tell application "System Events" to repeat with filepath in (get the ¬
POSIX path of the application file of every process whose ¬
background only is false and has scripting terminology ¬
is true)
try
tell my application filepath's document 1 to if ¬
exists then if path of (its properties ¬
& {path:""}) ≠ "" then set docpaths to ¬
docpaths & the path of every document ¬
in my application filepath
end try
end repeat
docpaths
I also coded it in a manner that would ordinarily have allowed for the removal of the try block, as the three possible causes of potential errors have either been taken out (non-scriptable applications) or prevented by validating both the existence of at least one documentand the resolution of its path property.
However, AppleScript bugs still prevent some applications, such as Safari, from enumerating all properties of any element where one of those properties is a URL. It throws the familiar error number -10000 indicative of an intrinsic AppleScript problem.
Also, some scriptable applications (e.g. CotEditor) store a document’s file reference in a file property element rather than a path property. But the method for retrieving those is much the same, after which the file references might need to be converted to posix paths if specifically desired.
tell application "System Events"
set appList to name of every application process whose background only is false and has scripting terminology is true and name is not "Finder"
end tell
set tid to text item delimiters
set text item delimiters to return
set openDocs to {}
repeat with appName in appList
set appName to contents of appName
set ascript to {"set openDocs to {}", "try", "tell application \"" & appName & "\" to set openDocs to path of documents", "on error errMsg", "log \"ERROR: \" & errMsg", "end try", "return openDocs"}
set ascript to ascript as text
set moreDocs to (run script ascript)
repeat with aDoc in moreDocs
set aDoc to contents of aDoc
if aDoc ≠ missing value then set end of openDocs to aDoc
end repeat
end repeat
set text item delimiters to tid
get openDocs