Before beginning this tutorial, please read the AppleScriptObjC Release Notes.
I just started playing around with the new AppleScriptObjC framework and I must say this is very cool.
Here is Salâs description from the release notes.
This takes AppleScript to a whole new level of functionality. In the past our only option to connect to the Cocoa world was through âcall method,â which I didnât care for, and it was confusing and difficult for most to use and understand. It was also limited in the Cocoa methods that were available to it. In addition, AppleScript Studio itself made the verbosity of AppleScript look trivial.
Although the tool we are presented with in Snow Leopard will require AppleScripters to learn some Cocoa, the end result is going to amaze you.
This will be a short tutorial to get you started using AppleScriptObjC in Xcode. We will create a project that has a text field, text view and a button.
To begin with the project will look like this.
Finished App
Here We GO!
Launch Xcode and create a new project. Select âCocoa - AppleScript Applicationâ and name it whatever you like.
Create a New Project
What You See
This is a full-blown Cocoa application! Awesome! We will be writing our code, for the most part in AppleScript but the application is all Cocoa.
Here is the list a default frameworks that get loaded. There is a tremendous amount of functionality and power here.
AppleScript File
There was one AppleScript file created. Its name is the project name with âAppDelegateâ appended to it. Click it to see its code in the editor.
Contents of AppleScript File
The Code
The code is pretty simple but you should notice a few things. The handlers now have and â_â underscore tacked on to the end as well as a parameter. More on this later.
We are going to add some code here to set our parent class. We do this by setting a property called âparentâ to the value of the class we want to inherit from. In our case this is NSObject. We add this just below our script opening declaration.
property parent : class "NSObject"
Next we add references to a text field and text view. In AppleScript Studio we would go into Interface Builder, add the text field and text view and give them AppleScript names. At this point we would have referenced them by saying something like:
Things start to get a little more hairy once that text field is moved into another view element.
With AppleScriptObjC this is a thing of the past. We create a reference to the object, in our case a text field, and from then on no matter how many sub views we bury it in, we still have access with a simple call.
Add these references, IBOutlets in Objective-C speak, below the parent declaration. We will hook these up in just a minute.
property textView : missing value
property textField : missing value
Next we will add a button declaration. In Objective-C speak this is an IBAction. The words IBAction and IBOutlet have special meaning in Objective-C for Interface Builder only. When we are in IB these are the properties we will have available to us.
Actions are formed a specific way. There is an underscore at the end of the name and only one parameter.
on setTextViewFromTextField_(sender)
end setTextViewFromTextField_
Letâs add some code to take the value we enter into our text field and put it into our text view.
on setTextViewFromTextField_(sender)
set textFieldValue to textField's stringValue()
textView's setString_(textFieldValue)
end setTextViewFromTextField_
Once we have hooked up our property âtextFieldâ to the actual text field in IB, it will be an NSTextField. When our application runs, it will create an instance of the NSTextField class. The two most common NSTextField methods are âstringValueâ and âsetStringValue.â In the code above we are sending a message to âtextFieldâ (an NSTextField object) and telling it to execute its âstringValueâ method. If you look in the documentation under NSTextField you will not find either of these methods. They are inherited from NSControl.
The same goes for the NSTextView. The âsetStringâ method is inherited from NSText. In this method we are sending a message to âtextViewâ (an NSTextView object) and telling it to execute its âsetStringâ method passing it the parameter âtextFieldValue.â
If you are wondering why there is not an underscore in âstringValue()â it is because there are no parameters for this method. More on this later.
What the Code looks like so far with a few comments
script PartOneAppDelegate
-- Inheritance
property parent : class "NSObject"
-- IBOutlets
property textView : missing value
property textField : missing value
-- IBActions (button clicks)
on setTextViewFromTextField_(sender)
set textFieldValue to textField's stringValue()
textView's setString_(textFieldValue)
end setTextViewFromTextField_
##################################################
# Application
on applicationWillFinishLaunching_(aNotification)
-- Insert code here to initialize your application before any files are opened
end applicationWillFinishLaunching_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return my NSTerminateNow
end applicationShouldTerminate_
end script
On to Interface Builder
Double-click on MainMenu.xib as shown below.
MainMenu.xib
If the main window is not already open then double-click it. Drag a text view, text field and button onto the layout and arrange them.
Window Layout
Connecting Objects
Looking in your MainMenu.xib window you will see a blue cube named after your project. Control-drag from the button to this object and release. Select âsetTextViewFromTextField_â from the menu that pops up. Now control-drag from the blue cube to the text field and choose âtextField.â Do the same for the text view. Thatâs all there is to it to connecting objects.
Connect Button to Object
Text View
When connecting the text view, be sure to connect to the text view inside the scroll view and not the scroll view.
Connect Text View
Build and Go
Weâve written the code and connected everything in IB so click the Build and Go button. Enter some text into the text field and when you click the button it will appear in the text view.
What Else?
Ok, so that was pretty easy but what else can we do? To start, I like to have a little room around my text inside a text view. Letâs make that happen.
Back in Xcode
We will add an awakeFromNib handler to put our code in. This way the text view is set up as our application is created. NSTextView has a method that we want to use.
The âvoidâ means that the function does not return a value and â(NSSize)â is the required parameter.
You may be asking about now, âWhat is an âNSSizeâ and how do I create one?â If you look up this method in the documentation youâll notice that âNSSizeâ in the method name is a link to its definition.
NSSize Definition
We see here that NSSize is a struct with two items of type CGFloat. To create this in AppleScript we use a list with two decimal numbers. Pretty easy.
AwakeFromNib Handler
While we are here, letâs add some default text to our text field.
on awakeFromNib()
textView's setTextContainerInset_({10.0, 10.0})
textField's setStringValue_("Default text")
end awakeFromNib
Select the Text Field on Application Launch
If you havenât noticed, each time we launch our application the mouse is in the text view and not in the text field. There are a few things we are going to do to fix this.
- Add a property as an outlet to our window
- Connect our property in IB using [control + drag] from the âblue cubeâ to our window.
- Use a Window method âmakeFirstResponder_()â and give it our text field as a parameter
-- add this below the other property declarations
property aWindow : missing value
-- add this as the last item in the awakeFromNib handler
aWindow's makeFirstResponder_(textField)
Final Code with a few comments
script PartOneAppDelegate
-- Inheritance
property parent : class "NSObject"
-- IBOutlets
-- Interface Builder considers an outlet as any
-- property with "missing value" as its value
property textView : missing value
property textField : missing value
property aWindow : missing value
-- IBActions (button clicks)
-- Interface Builder considers an action as any
-- single parameter method ending with an underscore
on setTextViewFromTextField_(sender)
set textFieldValue to textField's stringValue()
textView's setString_(textFieldValue)
end setTextViewFromTextField_
##################################################
# Application
on awakeFromNib()
textView's setTextContainerInset_({10.0, 10.0})
textField's setStringValue_("Default text")
aWindow's makeFirstResponder_(textField)
end awakeFromNib
on applicationWillFinishLaunching_(aNotification)
-- Insert code here to initialize your application before any files are opened
end applicationWillFinishLaunching_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return my NSTerminateNow
end applicationShouldTerminate_
end script
One Last Thing
Go back to Interface Builder, widen your main window and select the text field, text view and button. From the top menu choose âLayout â Embed Objects In â Box.â Adjust your window then build and run. Notice how everything still works. No code adjustment necessary. Go ahead, play around with this. Bury an object inside multiple levels of views and see that no code changes are required. You will get used to this functionality very fast.
Conclusion
Hopefully we have covered enough in this tutorial for you to see the power and potential in AppleScriptObjC. There is a learning curve but I truly believe after a few short months that anyone programming in AppleScriptObjC will not even consider going back to AppleScript Studio.
Update
If you would like to add to the text inside the text view try implementing something like this.
Notice that textViewâs |string|() method has a pipe on each side of the name. This is required
because âstringâ is an AppleScript keyword.
set theText to textField's stringValue()
set curText to textView's |string|()
tell class "NSString" of current application
set newText to its stringWithFormat_("%@%@%@", theText, return, curText)
end tell
textView's setString_(newText)
Until next time, happy coding.