Page 1 of 1

Fun with AppleScript (2nd Editon)

PostPosted: Wed Jun 02, 2010 6:11 am
by udo.killermann
Hello Board,

last time I showed how to script other applications. Now I want to share a project that shows how to make your Objective Basic application scriptable (this is not for the faint hearted). To learn about AppleScript from start to finish I recommend "AppleScript the definitive Guide, by Matt Neuberg, published by O'Reilly" and lots of experiments. In the advanced section Matt talks about scripting Cocoa apps and I took the code as a starting point.
The general idea is to set up an application that holds internal state via scriptText and scriptDebug. scriptText is a NSString and scriptDebug a Boolean. The application visualizes the state of theses properties in its only window. Here is some AppleScript code that drives the application:

Code: Select all
tell application "scriptApp"
   get scriptText
   set scriptText to "Hello!"
   set scriptDebug to true
   set scriptText to "Objective Basic is fun"
end tell

To make your app scriptable you need to define a dictionary that will be interpreted by AppleScript. I used Sdef Editor a programm created by Shadow Lab and distributed for free. The resulting XML looks like this:

Code: Select all
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="Dictionary">
   <suite name="scriptMe Suite" code="ScMe" description="Common classes and commands for script Me.">
      <cocoa name="NSCoreSuite"/>
      <class name="application" code="capp" description="The scriptMe&apos;s top-level scripting object.">
         <cocoa class="NSApplication"/>
         <property name="scriptText" code="ScTx" description="Some text usable in scriptMe" type="text"/>
         <property name="scriptDebug" code="ScDb" description="toggles debugging on and of" type="boolean"/>

Store the resulting sdef in your Resources directory. Now we have to extend Info.plist which is located in the Contents directory:
Code: Select all

Now everything behind the scene is ready for showtime. I add a NSWindow subclass in my project (called UKWindow) and use InterfaceBuilder to wire it as the application delegate (not shown here).

Here comes the UKWindowCode (with some comments), I'll attach the project as well:
Code: Select all
' idea by U. Killermann 2010

' this file is to test the scriptability of OB applications
' at the start I don't have any concrete idea how this might
' work

' adding the folling To Info.plist marks your application
' To be scriptable
'   <key>NSAppleScriptEnabled</key>
'   <string>YES</string>
' after compiling your application you can open it with
' AppleScript-Editor

' you can't do this (scripting) with a standard app

' <key>OSAScriptingDefinition</key>
' <string>scriptMe.sdef</string>
' allows you to narrow down the classes, properties and methods
' available in the AppleScript dictionary

' the window should show the internal state of the running
' application

' at the moment we only have to attributes, we can visualize
' sdef was created with the free Sdef Editor of Shadow Lab

IBOutlet UKscriptDebug As NSButton
Declare Class "NSApplication"

' here I store my internal state 
Dim myText As String

' if myDebug is set to YES we show calls to the setter and getter
' of scriptText
Dim myDebug As Boolean

' this delegate is invoked, when the script engine needs to know if the application handles
' a certain key requested by the running script
' it returns YES if the application handles a certain key

Declare Delegate "NSApplication" - (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key

' this delegate lies to the calling application by answering YES for any key, as the application
' only handles scriptText and scriptDebug as properties
Delegate application(sender As NSApplication, key As NSString) As Boolean
  Return Yes
End Delegate
' this method is invoked based on key value coding (KVC) for scriptText,
' the getter has no getZZZ but only ZZZ 
Function scriptText() As NSString
  If (myDebug) Then
    Alert("scripText requested:", myText)
  End If
End Function

' this method is invoked based on key value coding (KVC) for scriptText,
' the setter is named setZZZ
Sub setScriptText(theText As NSString)
 myText = theText
 UKscriptDebug.Title = myText
 If (myDebug) Then
  Alert("scriptText set to:", myText)   
 End If
End Sub

' this method is invoked based on key value coding (KVC) for scriptText,
' the getter has no getZZZ but only ZZZ 
Function scriptDebug() As Boolean
End Function

' this method is invoked based on key value coding (KVC) for scriptText,
' the setter is named setZZZ
Sub setScriptDebug(theDebug As Boolean)
  myDebug = theDebug
End Sub
' when the window comes into the foreground I show the current state of
' the properties, thus you can see the interaction between the script and
' the application
Event WindowDidBecomeKey()
  UKscriptDebug.Title = scriptText()
  UKscriptDebug.State= scriptDebug()
End Event

Event Init()
End Event

In the next step I want to add methods that the script can invoke. I already tried this, but this resulted in a corrupt dictionary - although its looking fine neither Sdef Editor nor AppleScript Editor can read it.

Again: Objective-Basic is fun to use and you can now code most of your ideas.

Kind regards

Re: Fun with AppleScript (2nd Editon)

PostPosted: Sat Jun 05, 2010 9:36 pm
by berndnoetscher
Was busy with a new feature. Having a preferences window and feature the easy way.
About your applescript effort. Thanks for code example. I am happy to help, but when I open applescript editor with "testTheApp", I got the following error.

Code: Select all
An error of type -10827 has occurred.

Any ideas?

Re: Fun with AppleScript (2nd Editon)

PostPosted: Sun Jun 06, 2010 6:01 am
by udo.killermann

I'll have a look! Did you have to tell the ScriptEditor, where scriptApp is located?

Kind regards

Re: Fun with AppleScript (2nd Editon)

PostPosted: Mon Jun 07, 2010 10:49 am
by berndnoetscher
Changed to have a full path for scriptApp

Code: Select all
tell application "/Users/berndnoetscher/Desktop/scriptApp"

But now it complains that about "get scriptText"

"The variable scriptText is not defined."

If I remove the line, it looks like it is running and I get an result in apple script editor "true". Nothing else. No window from scriptApp appearing?

Re: Fun with AppleScript (2nd Editon)

PostPosted: Wed Jun 09, 2010 6:43 am
by udo.killermann

my day time job is at the moment very demanding - I'll come back to this misbehaving example, when I have the right attitude.