ASObjC in Mavericks

Hi folks.

Can anyone with a Mavericks system or a long memory tell me if a script containing ASObjC in its source code can be compiled and run in Mavericks if it doesn’t actually try to execute the ASObjC stuff?

Background:
The scriptable genealogy software GEDitCOM II has an Extensions menu from which AppleScripts and its own “extensions” can be run. Three of the supplied extensions contain scripts in .applescript form which get data from the current GEDitCOM II document and compose beautifully styled reports in new Pages documents. They were written for Pages '09 and are useless to anyone stuck with the essentially unscriptable Pages 5. For my own education, I’ve adapted them to style the text in-house using ASObjC and to paste it into Pages when its complete. The adaptations work with both Pages '09 and Pages 5, produce exactly the same results (but without headers and footers), and are several times as fast as the originals.

I don’t know if I’ll bother offering them to the “GEDitCOM II community”, but they’re designed to be self-contained .applescripts like the originals, with the option of being useable in Mavericks if the user consents to the installation of the necessary library bundle. In Yosemite or later, a handler at the bottom of each script instances a script object containing the ASObjC code and this is used as a library. In Mavericks (or if a certain property value’s changed in the script), a different handler looks for the library in the user’s Script Libraries folder and, if it exists, employs a trick to ‘use’ it at that point. If the library’s not found, and the user consents, the script instances its on-board library, saves it as a script bundle in the Script Libraries folder, adds a suitable “Info.plist” file, and then 'use’s the bundle. It all works brilliantly when tested in El Capitan, and no doubt works in Yosemite too, but I’ve no idea if it actually works in Mavericks!

Below is a test script based on what happens in the adaptations. What I’d like to know is, in Mavericks:

  1. Does the script say anything?
  2. Does it successfully install a library bundle in the user’s Script Libraries folder?
  3. Does the trick to “use” the library work?
  4. Is the library viable when loaded that way? (Does the script display an upper-cased string?)
use AppleScript version "2.3.1"
use scripting additions

test()

on test()
	say "Well. I'm running."
	delay 1
	-- Check that the library we want to use is in the user's Script Libraries folder (if that exists).
	set pathToScriptLibrariesFolder to (path to library folder from user domain as text) & "Script Libraries:"
	set pathToTestLib to pathToScriptLibrariesFolder & "NG's Test Lib.scptd"
	tell application "System Events" to set libFound to (disk item pathToTestLib exists)
	-- If not, create it from the on-board library code.
	if (not libFound) then
		say "I can't find the library bundle, so I'll attempt to install one."
		delay 1
		-- Create the Script Libraries folder if it doesn't already exist.
		do shell script ("mkdir -p " & (quoted form of POSIX path of pathToScriptLibrariesFolder))
		
		-- Instance this script's on-board library and save it as a script bundle (courtesy of the ".scptd" name extension) in the folder.
		store script getLib() in file pathToTestLib
		
		-- Edit (or on my system create) the bundle's Info.plist file, including an entry enabling the bundle for ASObjC in Mavericks.
		set plistText to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
	<key>CFBundleIdentifier</key>
	<string>com.apple.ScriptEditor.id.ReportsToPagesOutputLib</string>
	<key>CFBundleName</key>
	<string>ReportsToPagesOutputLib</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>OSAAppleScriptObjCEnabled</key>
	<true/>
</dict>
</plist>
"
		set fRef to (open for access file (pathToTestLib & ":Contents:Info.plist") with write permission)
		try
			set eof fRef to 0
			write plistText as «class utf8» to fRef
		end try
		close access fRef
		
		say "OK so far, I think."
		delay 1
	end if
	
	say "I'm going to try loading the library."
	delay 1
	-- Con the Libraries system into loading the library only now (instead of when the script's compiled).
	set lib to lib of (run script "script
use lib: script \"NG's Test Lib\"
end script")
	
	say "And now to try using it."
	tell lib to |uppercase|("It works!  :)")
	display dialog result buttons {"OK"} default button 1
	
	say "Don't forget to delete the test library bundle when you've finished testing."
	tell application "Finder"
		reveal file pathToTestLib
		activate
	end tell
end test

on getLib()
	script -- A script object containing an ASObjC routine for testing.
		use AppleScript version "2.3.1"
		use framework "Foundation"
		
		on |uppercase|(txt)
			return (current application's class "NSString"'s stringWithString:(txt))'s uppercaseString() as text
		end |uppercase|
	end script
	return result
end getLib

Edit: Terminology correction in the query.

Hello Nigel

Here is a piece of text borrowed from Shane STANLEY’s book Everyday AppleScriptObjC 3ed edition available from :
http://www.macosxautomation.com/applescript/apps/everyday_book.html

THE ability to use AppleScriptObjC code outside Xcode projects was introduced in OS X 10.9, but how you can do it changed in 10.10. Importantly, the code you use is the same, and the mechanism used in 10.9 still works in later versions. It’s just that Yosemite introduced more direct support for AppleScriptObjC, making it simpler to use than in Mavericks.
To run AppleScriptObjC code in Mavericks, you need to store it in an AppleScriptObjC-based script library, which is simply a script bundle (.scptd le) with a minor modification to one of its les, stored in either a user’s or system’s Script Libraries folder, and in the case of applets and script bundles, alternatively embedded within their own Script Libraries folder.
Because script libraries were introduced at the same time as access to AppleScriptObjC, the two are often associated, but script libraries are a great way to share code whether it uses AppleScriptObjC or not. Reusable handlers are an enormous time-saver, and an excellent organizational aid.
You can, and often should, keep using script libraries for your AppleScriptObjC code. Indeed, using script libraries will be a requirement if you want to use AppleScriptObjC code in projects that also need to run under Mavericks. But the writing and development of AppleScriptObjC code can be more convenient when it can be done without the need to put handlers in one script and code that calls them in another. It becomes much easier to do things like quickly test a line or two of code.

The code in this book will be written around handlers, and generally presented so that they can be copied into a script editor window in OS X 10.10 or later and directly run. If you are running Mavericks, the code will need to be split into two scripts.

Your script make most of the needed job but it miss an important step :

¢ After you have saved the script, the Bundle Contents button in the script window’s toolbar will be active,
and you should press it. A drawer will open to the side.

This drawer no longer appear in El Capitan.

Oops. I missed the contents of the plist which your script write in the library.

Yvan KOENIG running El Capitan 10.11.6 in French (VALLAURIS, France) mercredi 20 juillet 2016 15:31:21

It seemed to function as expected on my 10.9.5 machine… text was spoken, library was created, and a message displayed IT WORKS!

Yvan and Marc. Thanks for your quick replies!

I know the scheme I described is something of an abuse of the Libraries system, but I thought it worth exploring, given the circumstances on the GEDitCOM II side.

Thanks for trying the test script, Marc. It’s great to know it does actually work on a Mavericks machine! :slight_smile:

Hello Nigel

I retrieved a disk with 10.9.5.
I booted from it and ran your script which worked.
To be sure that everything was OK, I extracted the short script below :

use AppleScript version "2.3.1"

use lib : script "NG's Test Lib"
use scripting additions

tell lib to |uppercase|("It works! :)")
display dialog result buttons {"OK"} default button 1

I behaved flawlessly so it’s clear that your plist file is what was needed to make the library active.

Yvan KOENIG running El Capitan 10.11.6 in French (VALLAURIS, France) mercredi 20 juillet 2016 16:11:27

Everything worked here on 10.9.5.

Thanks for the confirmation. :slight_smile:

Fascinating, as always. You could also load a library and get a reference like this:

set lib to item of item 1 of required import items of (run script "script
use script \"CheckVersion\"
end script")

That’s very arcane. Even Google’s never heard of it! :cool:

Presumably ‘required import items’ is some hidden AppleScript property introduced with Libraries.

required import items is only hidden in terms of being undocumented, and therefore theoretically subject to change. It holds all the use statement info in a list of records.

So if you run this:

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
use script "BridgePlus" version "1.3.2"
use application "Finder"
use application id "com.apple.systemevents"

required import items

The result will be:

{{version:"2.4"}, {item:framework "Foundation"}, {item:every scripting addition}, {item:script "BridgePlus", version:"1.3.2"}, {item:application "Finder"}, {item:application id "com.apple.systemevents"}}

And I forgot to mention: it’s a property of script objects, not AppleScript.