Sunday, April 18, 2021

#1 2021-01-13 02:46:21 pm

Fredrik71
Member
Registered: 2019-10-23
Posts: 671

NSWorkspace runningApplications

This examples use NSWorkspace to get information about runningApplications.

Applescript:

use framework "AppKit"

log runningApplicationsLocalizedName()
log runningApplicationsBundleIdentifier()
log runningApplicationsBundleURL()
log runningApplicationsExecutableURL()
log runningApplicationsLaunchDate()

on runningApplicationsBundleIdentifier()
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   set theList to {}
   repeat with anApp in workspace
       set theItem to anApp's bundleIdentifier() as text
       copy theItem to end of theList
   end repeat
   return theList
end runningApplicationsBundleIdentifier

on runningApplicationsBundleURL()
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   set theList to {}
   repeat with anApp in workspace
       set theItem to anApp's bundleURL()'s |path|() as text
       copy theItem to end of theList
   end repeat
   return theList
end runningApplicationsBundleURL

on runningApplicationsExecutableURL()
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   set theList to {}
   repeat with anApp in workspace
       set theItem to anApp's executableURL()'s |path|() as text
       copy theItem to end of theList
   end repeat
   return theList
end runningApplicationsExecutableURL

on runningApplicationsLaunchDate()
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   set theList to {}
   repeat with anApp in workspace
       set theItem to anApp's launchDate() as list
       copy theItem to end of theList
   end repeat
   return theList
end runningApplicationsLaunchDate

on runningApplicationsLocalizedName()
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   set theList to {}
   repeat with anApp in workspace
       set theItem to anApp's localizedName() as text
       copy theItem to end of theList
   end repeat
   return theList
end runningApplicationsLocalizedName

Choose PID and it will return bundleURL path.
https://developer.apple.com/documentati … guage=objc

Applescript:

use framework "AppKit"
use scripting additions

runningApplicationsProcessIdentifierWithPath()

on runningApplicationsProcessIdentifierWithPath()
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   set theList to {}
   repeat with anApp in workspace
       set theItem to anApp's processIdentifier() as text
       copy theItem to end of theList
   end repeat
   set theProcessID to (choose from list theList) as text
   set theApp to current application's NSRunningApplication's runningApplicationWithProcessIdentifier:theProcessID
   return theApp's bundleURL()'s |path| as text
end runningApplicationsProcessIdentifierWithPath

Input bundleIdentifier, return a string

Applescript:

use framework "AppKit"

my runningApplicationsWithBundleIdentifier:"com.apple.Terminal"

on runningApplicationsWithBundleIdentifier:_bundleIdentifier
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   repeat with anApp in workspace
       if _bundleIdentifier is (anApp's bundleIdentifier() as text) then
           set theString to (anApp's localizedName() as text) & space
           set theString to theString & (anApp's bundleURL()'s |path|() as text) & space
           set theString to theString & (anApp's launchDate() as list) & space
           set theString to theString & (anApp's isHidden() as boolean)
       end if
   end repeat
   return theString
end runningApplicationsWithBundleIdentifier:

Input bundleIdentifier, return a list.

Applescript:

use framework "AppKit"

my runningApplicationsWithBundleIdentifier:"com.apple.Terminal"

on runningApplicationsWithBundleIdentifier:_bundleIdentifier
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   repeat with anApp in workspace
       if _bundleIdentifier is (anApp's bundleIdentifier() as text) then
           set theName to (anApp's localizedName() as text)
           set theBundleURL to (anApp's bundleURL()'s |path|() as text)
           set theLaunchDate to (anApp's launchDate() as list)
           set theHidden to (anApp's isHidden() as boolean)
       end if
   end repeat
   return {theName, theBundleURL, theLaunchDate, theHidden}
end runningApplicationsWithBundleIdentifier:

With Shane's Myriad Tables Lib

Applescript:

use framework "AppKit"
use script "Myriad Tables Lib" version "1.0.9"

my runningApplicationsWithBundleIdentifier:"com.apple.Terminal"

on runningApplicationsWithBundleIdentifier:_bundleIdentifier
   set workspace to (current application's NSWorkspace's sharedWorkspace)'s runningApplications()
   repeat with anApp in workspace
       if _bundleIdentifier is (anApp's bundleIdentifier() as text) then
           set theName to (anApp's localizedName() as text)
           set theBundleURL to (anApp's bundleURL()'s |path|() as text)
           set theLaunchDate to (anApp's launchDate() as list)
           set theHidden to (anApp's isHidden() as boolean)
       end if
   end repeat
   set theData to swap columns and rows in {theName as list, theBundleURL as list, theLaunchDate as list, theHidden as list}
   set theTable to make new table with data theData column headings {"Name", "Location", "Date", "Hidden"}
   modify table theTable with alternate backgrounds
   display table theTable
end runningApplicationsWithBundleIdentifier:

Last edited by Fredrik71 (2021-01-14 04:19:20 am)


if you are the expert, who will you call if its not your imagination.

Offline

 

#2 2021-02-25 02:56:43 am

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 149

Re: NSWorkspace runningApplications

TL;DR:

    1) Less is more where lists are concerned, which is ironic given the length of my explanation
    2) Ditto with repeat loops, which are thankfully not required here

List mutation

Did you know it's possible to overwrite a list that you loop over rather than creating a new list and appending the items to it ?  There's certainly instances where overwriting wouldn't be what you would want, especially if need to use the original data again later in a script.  But, given that's not the case here, rather than doing this:

Applescript:

set theList to {}
repeat with anApp in Workspace
       set theItem to anApp's bundleIdentifier() as text
       copy theItem to end of theList
end repeat

you can do this:

Applescript:

set Workspace to NSWorkspace's sharedWorkspace() in the current application
set bundleIDs to runningApplications() in the Workspace as list

repeat with A in the bundleIDs
       tell A to set its contents to the ¬
               bundleIdentifier() as text
end repeat

return the bundleIDs

Although, bear in mind that runningApplications() returns an NSArray object, which are inherently immutable, so in order to overwrite items, you need it in a form that is mutable: the two immediately available options are an AppleScript list, or an NSMutableArray (by way of mutableCopy()).  Either will do, and I doubt there's any speed difference, since the repeat loop is our bottleneck.

The reasons why one might elect to overwrite a list rather than create and copy items across are space and time: lists are not trivial data structures, and they take up memory, so two take up approximately twice as more (not always true, but it probably would be here); it also slows things down, because AppleScript is reading from one, but then having to write to another.  The use of copy, of course, doesn't help things—it's there to create a distinct copy of data to assign to a variable, which means it dereferences it, duplicates it, and puts it somewhere, but it's slow and isn't often necessary unless you are dealing specifically with AppleScript data types that preferentially pass around references that all point back to the same, original source (date, record, and list class data spring to mind), partly, I imagine, to save space, but it's also a useful feature when you want it.  The rest of the time, it's easy to dereference explicitly by way of contents of... and/or get, or with coercion, which is exactly what you did to unwrap the NSString returned by bundleIdentifier() that as text necessarily created distinct data object that could be appended using set.  One would hope that AppleScript's compiler isn't stupid enough to be given something it just created one line previously then create another copy like a mindless machine, and then destroy the one left over…I can be confident that other compilers are a bit smarter, but I have seen speed differences between copy and set that one wouldn't expect should be apparent unless AppleScript performs operations that are redundant.

Another example (not sure if true, but someone who knows their shit more than I) said that if you give AppleScript a text class bit of text, and then ask it to coerce its class to text, that's what it does.

Property Getting For Collections of Objects

Anyway, about that bottleneck I mentioned, the main thing I originally intended to highlight was that, despite over-detailing all of the above, for what your handlers have set out to do, it's possible to do without the need for any loops or list manipulation.  Objective-C methods that don't take any parameters can typically be called in two ways.  In AppleScript, we can treat the call like a function/handler, which is often the most convenient for returning data in a more friendly way that's immediately viable in AppleScript, which saves ballsing around with opaque references before being able to use it:

Applescript:

tell NSEvent in the current application
       return the mouseLocation()
end tell --> {x:286.8828125, y:661.6328125}

.  Accessing it as a property firstly requires—in the tell block scenario—that we be explicit with what we want, or it assumes the identifier is referring to a variable that it will assume is defined in the scope that the tell block itself is calling from rather than the scope of the target it's calling to; and secondly:

Applescript:

tell NSEvent in the current application
       return its mouseLocation
end tell --> «class ocid» id «data optr00000000608FB50500600000»

But, you couldn't do this with a function call:

Applescript:

return the bundleIdentifier of the sharedWorkspace()'s ¬
       runningApplications() in the current application's ¬
       NSWorkspace as list

No repeat loops, no arrays/lists that need to be overwritten or copied, but only when called with no parentheses.  So, if you wanted a blazingly fast, and elegantly short script to perform all the enumeration of these handlers in a line:

Applescript:

use framework "Foundation"
use framework "AppKit"

property this : a reference to current application
property NSArray : a reference to NSArray of this
property NSWorkspace : a reference to NSWorkspace of this
property Workspace : a reference to the sharedWorkspace in NSWorkspace
property runningApplications : a reference to runningApplications in the Workspace

tell (NSArray's arrayWithArray:(runningApplications's [localizedName, ¬
       bundleIdentifier, bundleURL's |path|, executableURL's |path|, ¬
       launchDate])) as list to {name:item 1, id:item 2, path:item 3 ¬
       , file:item 4, date:item 5}

Offline

 

#3 2021-02-26 03:13:33 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 671

Re: NSWorkspace runningApplications

Thanks for sharing CK, interesting reading.

The inspiration of my handlers come from other site. The original code was in PyObjC.

My approach to learn more AppleScriptObjC is to convert PyObjC and Objective-C examples.
I have also try to convert Swift code to AppleScriptObjC.

To understand the difference, the learning will be faster to the approach we choose.

I have use copy in repeat loop before and in most cases it have not been any problem.
You make a strong point, and I agree with you.

All that said, I find your approach and last script very interesting and fast as you said.

Thanks, CK

Last edited by Fredrik71 (2021-02-26 03:29:01 am)


if you are the expert, who will you call if its not your imagination.

Offline

 

#4 2021-02-26 05:46:55 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6615

Re: NSWorkspace runningApplications

FWIW, using property names without parentheses is the same as using valueForKey:. And because arrays support valueForKey: (and valueForKeyPath:), they allow you to skip repeat loops.

So:

Applescript:

use framework "Foundation"
use framework "AppKit"

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set theNames to (theApps's valueForKey:"localizedName") as list
set theIDs to (theApps's valueForKey:"bundleIdentifier") as list
set thePaths to (theApps's valueForKeyPath:"bundleURL.path") as list
set theFiles to (theApps's valueForKeyPath:"executableURL.path") as list
set theDates to (theApps's valueForKey:"launchDate") as list
return {name:theNames, id:theIDs, path:thePaths, file:theFiles, date:theDates}


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#5 2021-02-26 10:56:37 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 671

Re: NSWorkspace runningApplications

Thanks Shane,

How would you include sort in your code ??

In other words how do you sort a array that include valueForKey.
I try to sort theApps but that didn't do anything.

Regards.


if you are the expert, who will you call if its not your imagination.

Offline

 

#6 2021-02-26 04:34:38 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6615

Re: NSWorkspace runningApplications

What are you trying to sort by?


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#7 2021-02-26 06:23:13 pm

Fredrik71
Member
Registered: 2019-10-23
Posts: 671

Re: NSWorkspace runningApplications

Shane: I was trying to sort by LocalizedName.


if you are the expert, who will you call if its not your imagination.

Offline

 

#8 2021-02-26 11:22:21 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6615

Re: NSWorkspace runningApplications

Applescript:

use framework "Foundation"
use framework "AppKit"

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set theDesc to current application's NSSortDescriptor's sortDescriptorWithKey:"localizedName" ascending:true selector:"localizedCaseInsensitiveCompare:"
set theApps to theApps's sortedArrayUsingDescriptors:{theDesc}


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#9 2021-02-27 02:09:39 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 671

Re: NSWorkspace runningApplications

Thanks, Shane

The code I try was from this post: https://stackoverflow.com/questions/242 … alueforkey

I did something similar to your code but I forgot the selector:"localizedCaseInsensitiveCompare:"

Here is Shanes code with sort: localizedName

Applescript:

use framework "Foundation"
use framework "AppKit"

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set theDesc to current application's NSSortDescriptor's sortDescriptorWithKey:"localizedName" ascending:true selector:"localizedCaseInsensitiveCompare:"
set theApps to theApps's sortedArrayUsingDescriptors:{theDesc}

set theNames to (theApps's valueForKey:"localizedName") as list
set theIDs to (theApps's valueForKey:"bundleIdentifier") as list
set thePaths to (theApps's valueForKeyPath:"bundleURL.path") as list
set theFiles to (theApps's valueForKeyPath:"executableURL.path") as list
set theDates to (theApps's valueForKey:"launchDate") as list
return {name:theNames, id:theIDs, path:thePaths, file:theFiles, date:theDates}

Last edited by Fredrik71 (2021-02-27 02:13:32 am)


if you are the expert, who will you call if its not your imagination.

Offline

 

#10 2021-02-28 10:28:54 pm

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 149

Re: NSWorkspace runningApplications

Shane Stanley wrote:

FWIW, using property names without parentheses is the same as using valueForKey:. And because arrays support valueForKey: (and valueForKeyPath:), they allow you to skip repeat loops.

I love skipping repeat loops.  It's like the heroin of AppleScript.

Is valueForKey: preferred to using property names?  On the one hand, the syntax for property getting is more compact/readable and can be declared themselves as properties of a script object, if that's in any way useful.  On the other hand, an AppleScript identifier isn't as flexible as a string-based key path that can be assembled during runtime.  But they're both doing the same behind the scenes ?

Offline

 

#11 2021-02-28 11:02:51 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6615

Re: NSWorkspace runningApplications

CK wrote:

But they're both doing the same behind the scenes ?



Yes. Text keys have the other advantages of being joinable into keypaths, being exempt from the risk of AppleScript's insistence on consistent capitalisation of identifiers, and safe from terminology clashes.

Putting ASObjC-related objects in script objects is risky because they then can't be flattened/stored.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#12 2021-02-28 11:11:35 pm

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 149

Re: NSWorkspace runningApplications

Shane Stanley wrote:

Putting ASObjC-related objects in script objects is risky because they then can't be flattened/stored.

Ah, good point.  That rings a bell.  I think that could have been a reason I tried and ended up not when I had a script object obsession.

Shane Stanley wrote:

AppleScript's insistence on consistent capitalisation of identifiers,

Really irksome.  I loathe needless redundancies/restrictions purposely built-in to some languages.

I'll go with valueForKey:/-Path: from here on out.  Thanks for the eye-opening.

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)