Jonathan 'Wolf' Rentzsch wrote:

On Mar 1, 2008, at 7:28 PM, Hannes Petri wrote:
iTunesApplication *iTunes = [[SBApplication alloc] initWithBundleIdentifier:@"com.apple.iTunes"];
iTunesTrack *currentTrack = [iTunes currentTrack];

if ([currentTrack isKindOfClass:[iTunesFileTrack class]]) {
…
}

The problem is, that the class of the object returned is _always_ iTunesTrack, and not iTunesFileTrack, as i expect it to be. If i run the following applescript code:

tell application "iTunes" to current track

I get a "file track", which makes it possible to fetch the path using the "location" attribute. If I, in the ObjC example, try [currentTrack location], I'm told that it doesn't respond to that selector. I have made certain that the object is of class iTunesTrack by typing 'po [currentTrack class]' in gdb.

I ran into the same thing -- Scripting Bridge may play games isKindOfClass: that bite us.

Short explanation: Scripting Bridge is made of LAME and FAIL.

Longer explanation:

The fundamental problem with Scripting Bridge is that it basically lies about how application scripting works.

The key concept to grasp is that Apple event IPC is based on RPC plus simple relational queries. It is *not* an object-oriented IPC mechanism à la DO/COM/CORBA/etc.

For convenience and conciseness, AppleScript implements a couple of magical behaviours at the compiler/interpreter level and dresses the whole lot up in an OO-like syntax. This OO resemblance is quite superficial, however, and beneath the surface the original RPC+query semantics are preserved intact. This makes AppleScript easier to use, at least for the common-case usages. The tradeoff is that, because the syntax obfuscates the semantics, it's difficult for users to form a complete, accurate understanding of how Apple event IPC actually works. Still, for the majority of AppleScript users, even a flawed, incomplete mental model is usually 'good enough' to get the results they need, so while these obfuscations do cause some problems they aren't disastrous.

Scripting Bridge, however, goes much further. Not only does it apply a generous amount of OO-like syntactic sugar on the underlying RPC+query mechanism, it also tries to impose its own set of OO-like semantics as well. This is a Bad Idea for various reasons, many of which previously I've discussed here and elsewhere (Google if you want to read more).


In this particular case, the first major problem is that because SB creates 'proxy classes' based on the class definitions in the application's dictionary, users naturally assume that instances of these classes directly represent objects in the scriptable application. This is, after all, how Distributed Objects and other object-oriented IPC mechanisms do things, after all: call a method on a proxy object, and the same method is invoked on the remote object and its result returned.

Alas, this assumption is completely and utterly wrong. Remember, Apple event IPC is RPC+queries, *not* OOP. While SB objects may look and superficially behave like DO-esque proxy objects, they are actually just thin wrappers around Apple event queries, dressed up to look like something else. Apple event queries have no real meaning in themselves; it's only when you stick them in an Apple event and send them to an application that they are evaluated to identify actual application objects. Therefore, by extension, there is no direct relationship between an SB object and an application object: calling a method on an SB object is not guaranteed to invoke an operation on a remote object; heck, an SB object isn't even guaranteed to map to a specific remote object, or the same object as the last time it was used, or any object at all!


Conversely, when you ask Scripting Bridge for [iTunes currentTrack], SB doesn't actually know what iTunes current track is, or what class it is, or even if it exists at all. To get that kind of information, you have to ask the application itself by sending it the relevant Apple event. All SB does is construct an Apple event query identifying the application object's 'current track' property and stuffs it in one of its faux-proxy objects for the client to manipulate further.

Which brings us to the second major problem: in order to present its pseudo-OO API, Scripting Bridge uses the type information given in the application dictionary and extracted from Apple event queries to determine which class of wrapper to put around a given query object. This wrapper then determines which operations you can perform using that query object - which would be perfectly okay if Apple event IPC followed OO rules, but it doesn't. As long as an Apple event query is grammatically correct it's perfectly legal to use, and it's up to the application evaluating it to decide what, if any, object(s) it identifies.

For example, 'document 1 of name of character "foo" of color of application "TextEdit"' is a completely legal query; it just won't locate a valid object when sent to TextEdit to evaluate, resulting in a runtime error. At first glance, this looks like an opportunity for Scripting Bridge to 'improve' on AppleScript by having the compiler prevent such nonsensical requests from ever being constructed. Unfortunately, the current laissez-faire approach taken by Apple event IPC - and, by extension, all of the applications that implement it - just weren't designed with this kind of usage in mind, and neither application dictionaries and implementations are guaranteed to be sufficiently detailed or accurate to reliably enforce such rigourous restrictions.

As a result, there's no guarantee that the class of object returned by [iTunes currentTrack] or any other property or element method bears any resemblance to the class of object that will actually be operated on when the query is evaluated. SB just creates a wrapper object of whatever class is specified by the 'current track' property in iTunes' dictionary. Since the dictionary says this property's type is track', the result is an instance of SB's 'iTunesTrack' class. Call its -class method, and you'll _always_ get 'iTunesTrack', regardless of what iTunes is playing.

Furthermore, if the class of object indicated by the 'current track' property in iTunes' dictionary doesn't define the properties or elements you want to use (in this case, 'location', then SB won't allow you to construct a query using those properties. Thus, even if you already *know* that iTunes is playing a file track, you still can't ask SB for [[iTunes currentTrack] location] because -location isn't a method on the 'iTunesTrack' object that SB previously returned. Your only option, short of resorting to cryptic four-char codes to build your query, is to send a 'get' event to iTunes asking it to locate the currently playing track ([[iTunes currentTrack] get]) and return a query to that, and hoping that the query it constructs contains sufficiently specific type information that SB will create a wrapper object of the class you need ('iTunesFileTrack') in order to find out the track's exact class and get its location if it's a file track. (Which, fortunately for SB, iTunes does; although there are some apps, e.g. Excel, that resolutely refuse to be specific.)


None of which is a problem in AppleScript, mind, since it has the good sense to treat Apple events more or less on their own terms even with the syntactic sugar:

        tell application "iTunes"
                if class of current track is file track then
                        location of current track
                end if
        end tell

Ditto appscript, which learned these lessons years ago and is, if anything, even truer to Apple event semantics than AppleScript, e.g.:

        t = app('iTunes').current_track
        p t.location.get if t.class_.get == :file_track

Ironically, the principles behind Apple event IPC are really rather simple (if unusual), and the main reason so many folk struggle to make sense of it is that Apple have done an fantastically dreadful job of explaining it over the last decade-plus. Unfortunately, with Scripting Bridge, it looks as if instead of learning from and addressing those previous mistakes, Apple are determined to compound them with a whole new generation of really bad decisions. But it is their OS, and for those that wish a more reliable alternative to AppleScript there's always Python/Ruby/ObjC appscript, of course.

HTH

has
--
http://appscript.sourceforge.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to