Re: Problems with ScriptingBridge and iTunes
Re: Problems with ScriptingBridge and iTunes
- Subject: Re: Problems with ScriptingBridge and iTunes
- From: has <email@hidden>
- Date: Sun, 2 Mar 2008 23:02:37 +0000
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 (email@hidden)
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:
This email sent to email@hidden