简体   繁体   中英

ScriptingBridge - How Does It Work “Behind the Scenes”

Context: I'm working on a Pharo/Smalltalk -> Objective-C bridge

Scenario: In the following Objective-C ScriptingBridge snippet:

iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];

iTunesTrack *currentTrack = iTunes.currentTrack; //[1]
// This low level way works too
//iTunesTrack *currentTrack = [iTunes propertyWithCode: 'pTrk']; //[2]

[iTunes playpause]; //[3]

Problem: The bridge uses class_getInstanceMethod to determine if an object understands a message/selector, but it returns NULL for scripting messages like playpause

Question #1 Why does class_getInstanceMethod return NULL for scripting messages like playpause ? Same question for class_copyMethodList ? What is special about scripting messages that they do not act like other Obj-C messages (except when they do?)?

Question #2 [SOLVED - see @Matt's answer]

Where, as per the docs , in the "dynamically defined subclass for the iTunes application" does SB put the "application-specific methods that handle the sending of Apple events automatically"? And, given that class_getInstanceMethod fails to find this behavior (see below), what's a reliable way for the bridge to test for it (ie whether such a method/message exists)?

The Objective-C Runtime API reports mixed results. On one hand, the iTunesApplication class seems not to have any methods (or properties for that matter):

  • class_copyMethodList([iTunes class]... returns zero methods
  • class_getInstanceMethod , which the bridge uses to find and execute methods, fails.

On the other, #playpause can be queried and sent through other parts of the API:

  • respondsToSelector: -> TRUE
  • methodSignatureForSelector: returns a signature
  • and performSelector: actually sends the message

Strangely, methodForSelector:@"playpause" successfully returns an IMP in Obj-C, but crashes if sent from the other side of the bridge.

Question #3 [SOLVED]

How one would simulate/replicate [3]?

Answered by @Willeke in comments: [iTunes sendEvent:'hook' id:'PlPs' parameters:0]

If SB doesn't use Objective-C messages, what do the docs mean by "subclasses of SBApplication implement application-specific methods that handle the sending of Apple events automatically"? Why does iTunes respondsToSelector: @"playpause" work ie return true? And how does [iTunes playpause] work? Etc, etc..

It works because the very first thing you do in a scripting bridge application is generate a header. In Catalina you do it like this:

sdp -f h --basename iTunes /System/Applications/Music.app/Contents/Resources/com.apple.Music.sdef

This reads the iTunes dictionary ( sdef ) and generates the header for a group of analogous Objective-C classes. Now you have an iTunes.h file that you include in the application project and import in your code. It contains this line:

- (void) playpause;  // toggle the playing/paused state of the current track

So now playpause is declared, explicitly, as a legal command that you can send to an iTunesApplication object. Then, when you actually run your application, you say

iTunesApplication* tunes = (iTunesApplication*)[SBApplication applicationWithBundleIdentifier:@"com.apple.music"];

This causes your application to talk to iTunes (Music) and get the dictionary ( sdef ) again , generating the implementation for the methods declared in the header. The implementation for the playpause command is exactly what the sdef says it should be: namely, to send the hookPlPs event to iTunes.

So that explains both why you are allowed to say playpause and what happens when you say it.

That is what AppleScript is — it's an application supplying a list of things you can say to it using Apple events, along with English-like terms that reference those Apple events.

So if you want to write a bridge, you have to do the same thing: you need to provide a way to scour the sdef resource of a target application and translate that information into a way for corresponding commands to be given in your language, whatever it is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM