Vojto
Vojto

Reputation: 6959

AppleScript is cool. Can I do the same thing in plain Objective-C?

so I'm working on my little Mac app, and I want control Adium with it. AppleScript is very cool:

tell application "Adium"
    go away with message "Zoned in for Maths."
end tell

(If you're wondering what this is supposed to be. In order to actually start studying I need to create application that will change my IM status, of course ... )

I tried it in Script Editor, it worked, and I'm pretty sure that calling AppleScript from Cocoa application is gonna be trivial.

But.

Is AppleScript the only way? I don't mind using AppleScript, but it looks like programming for noobs.

Is there any way to do the same thing as above code does without AppleScript, in plain Objective-C somehow?

Would someone point me to relevant documentation? I tried Google but it was like I don't even know what I'm looking for.

Thanks!

Upvotes: 2

Views: 1071

Answers (3)

Clark
Clark

Reputation: 818

Others have mentioned sending full Applescript or using the Scripting Bridge. A third choice is to use Appscript which is also available for Python and Ruby. It is a little cleaner (IMO) than using the Scripting Bridge in some ways. And definitely easier. Although in other ways Scripting Bridge is better. It also has the advantage of an application called ASTranslate which will translate most Applescript calls into Appscript. Here's the Appscript for your little Applescript example.

ADApplication *adium = [ADApplication applicationWithName: @"Adium"];
ADGoAwayCommand *cmd = [[adium goAway] withMessage: @"Zoned in for Maths."];
id result = [cmd send];

Upvotes: 1

outis
outis

Reputation: 77450

The simplest way would be to use NSAppleScript

NSAppleScript *script = [[NSAppleScript alloc] 
    initWithSource:@"tell application \"Adium\" to go away with message \"Zoned in for Maths.\""
];

For a more powerful way of accessing scripting, use Scripting Bridge. Scripting Bridge requires at least the 10.5 SDK. You first need to prepare your app.

  1. Use the sdef and sdp command line utilities to generating header files for the applications you wish to control (see "Preparing to Code" for details).
  2. Add the generated header to your project.
  3. Add the ScriptingBridge framework to your project. After that, you can use Objective-C calls to control the other application.

After that, you can use Objective-C to send scripting commands to the application.

AdiumApplication *adium = [SBApplication applicationWithBundleIdentifier:@"com.adiumX.adiumX"];
for (AdiumAccount* acct in [adium accounts]) {
    [acct goAwayWithMessage:(AdiumRichText *)@"Zoned in for Maths."];
}

Status messages are Adium's rich text type (which is NSTextStorage under the hood), but it's convertable from plain text, so passing an NSString rather than a true AdiumRichText should work fine.

There are a few hoops to jump through. For example, you can't create scripting objects in the target application by using its ObjC classes directly; you must use classForScriptingClass: to get the class, which you can then use to create objects as normal (i.e. alloc and init, initWithProperties &c.).

// creating an AdiumContactGroup
NSDictionary *props = [NSDictionary 
    dictionaryWithObjectsAndKeys:
        @"mathies",@"name",
        nil
];
AdiumContactGroup *mathies= [[[[adium classForScriptingClass:@"contact group"] alloc] 
                              initWithProperties:props]
                             autorelease];
if (mathies) {
    [[adium contactGroups] addObject:mathies];
}

Note that other languages (such as Python and Ruby) also have Scripting bindings.

Upvotes: 7

Yuji
Yuji

Reputation: 34195

Interprocess communication in Mac OS X is done by something called Apple Events. AppleScript is one way to send and receive Apple Events to other applications.

Therefore, you just need to construct Apple Events directly and send it to the other app, from Objective-C or whatever other language.

Honestly, if you just want to change the status of Adium, it's easiest to use NSAppleScript and pass what you just wrote, from inside Objective-C.

If you want to do more complicated stuff, Scripting Bridge is the way to go. This mechanism maps Apple Events' object hierarchy to Objective-C's object hierarchy.

If you think that's still a newbie's way, you should directly create Apple Events via NSAppleEventDescriptor.

Well, some of us old timers think using Objective-C is a sissy's way. If you think so, you should directly deal with C structs called AEDesc and such. See Apple Events programming guide and the corresponding reference.

However, I think people who use OS X are all noobs. Real people use Linux.

My dad would say people who use GUI are just too spoiled.

The point is, you don't have to care whether it is a newbie's way or not. The important thing is whether you can achieve what you want. In fact, AppleScript is a very powerful, dynamical language, whose power is not well appreciated by many people. Read AppleScript language guide and be surprised.

Upvotes: 10

Related Questions