Wednesday, October 29, 2014

AppleScript and Yosemite

Ray Robertson:

Apple introduced a great variety of new automation features and updates in Yosemite. I’ve written up a quick summary below with links to more detailed information.

Daniel Jalkut:

Unfortunately the progress feature of AppleScript has not been exposed to 3rd party developers, so far as I can tell.

AppleScript Release Notes:

AppleScript/Objective-C is now available to all scripts, not just library scripts.

I’ve been wanting this feature for years. You no longer have to create a special AppleScriptObjC application; you can use it from any script. This will be useful both for the powerful Cocoa APIs as well as the many basic data structures and operations that were never part of AppleScript.

Here are some examples:

use framework "Foundation"

-- Calling an Objective-C class method:
get current application's NSDate's timeIntervalSinceReferenceDate()

-- Use pipes to avoid conflicts with AppleScript keywords.
get current application's NSDate's |date|'s timeIntervalSinceReferenceDate()

-- Basically, you just use “'s” in place of “.”.
set _array to current application's NSMutableArray's alloc()'s init()

-- “count” is also reserved.
get _array's |count|()

-- AppleScript changes “_array's addObject_(1)” to the interleaved syntax:
_array's addObject:1

-- AppleScript changes “_array's insertObject_atIndex_(0, 0)” to:
_array's insertObject:0 atIndex:0

set _string to _array's |description|()
set _appleScriptString to _string as Unicode text

set _data to _string's dataUsingEncoding:(current application's NSUTF8StringEncoding)
set _tildePath to current application's NSString's stringWithString:"~/Desktop/test"
set _path to _tildePath's stringByExpandingTildeInPath()
set {_ok, _error} to _data's writeToFile:_path options:0 |error|:(reference)
-- Should return “{1, missing value}”.
-- The file will contain “(0, 1)”.

Unfortunately, it looks like the technology is not yet very mature. Script Editor beach balled and had to be force-quit half a dozen times while I was writing this sample.

Update (2014-10-31): Joris Kluivers shows that developers can get at the progress of a script—via NSProgress.

Update (2014-11-29): Mark Aldritt:

What is not explained is how a host application can access this information and display a script’s progress in its own UI. Here’s how you do it.

At intervals the script will call the OSAActiveProc. Within this callback function you can make OSA calls that access the running script’s state. You start by getting a reference to the ‘AppleScript’ object. From there you can read the value of the four progress properties.

5 Comments RSS · Twitter

Getting the products to work in a reliable fashion is the real problem right now. Ideally what Apple should do is make Swift a full scripting language for these sorts of things. Right now trying to use Swift to do these sorts of things is still more of a nightmare than it has to be. Although it has gotten slightly better. But nothing compared to what Python + Appscript used to offer.

@Clark I really don’t see how Swift can be used as a scripting language. Even with the most basic tasks, it seems like you really have to help out the type inferencer. Then add Scripting Bridge to the fun…

I just want the bridge to work well from AppleScript. Looking at samples in Activity Monitor, it looks like the problem I ran into isn’t even due to AppleScript—rather, some sort of Extensions issue.

@Clark: what Michael says. For scripting, where you're often throwing together some quick-n-dirty code to run once or twice then throw away, you really want a simpler, tighter language with soft typing and a degree of exception handling. (What I'd really like is a scripting language that ditches all the bureaucratic syntactic and semantic baggage that's accreted from the Algol/C world, and returns to the laser-sharp parsimony of Logo/Smalltalk.)

@Michael: 10.10's elimination of the painful limitations on AS-ObjC integration is belated but welcome. One thing I've found is that using ASOC from Python via PyObjC is surprisingly easy. Stick one or more .scpt files containing ASOC classes into a folder, e.g.:

use framework "Foundation"
use scripting additions

script ASOCTest
   property parent : class "NSObject"
   --
   on test()
      activate
      display dialog "Hello from AppleScript!"
   end test
end script

then just use it from Python like this:

from Foundation import NSBundle, NSClassFromString
import AppleScriptObjC

NSBundle.alloc().initWithPath_(FOLDERPATH).loadAppleScriptObjectiveCScripts()

ASOCTest = NSClassFromString(u"ASOCTest") # get the ASOC class...
ASOCTest.alloc().init().test() # ...then instantiate and call it

Not as clean as a native bridge, natch, but it's all 100% currently supported APIs with no 3rd-party dependencies and lets you use AppleScript for what it's good at (application scripting) and Python for everything else.

(Apologies for glitchy formatting; not sure what the correct tags should be.)

@has Thanks for the PyObjC example. I changed the tags to <pre>.

Leave a Comment