Skip to content

ScriptingBridge Support #3

@seandenigris

Description

@seandenigris

Here is a sample acceptance test usage in Python (see reference):

from Foundation import *
from ScriptingBridge import *
iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
print iTunes.currentTrack().name()

The first three lines are no problem. Foundation is already loaded and then in Pharo, we can do:

DynamicLoader loadLibrary: '/System/Library/Frameworks/ScriptingBridge.framework/ScriptingBridge'.
iTunesID := 'com.apple.iTunes' asNSString autorelease.
iTunes := (#SBApplication inObjCFramework: 'ScriptingBridge')
		applicationWithBundleIdentifier: iTunesID.

The problem is the last line. What we'd like to write is:

iTunes currentTrack name.

However, the bridge uses class_getInstanceMethod to determine if an object understands a message/selector, and it returns NULL for scripting messages like currentTrack and playpause.

I've asked on SO for more information about how SB works its magic. Here are some parts of the Objective-C API that do see these scripting methods:

  • respondsToSelector: -> TRUE
  • methodSignatureForSelector: - does return 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.

Possible Solutions

Perform Selector

iTunes performSelector: #playpause asObjCSelector.

This works, but requires inheritance from NSObject and the docs suggest this approach may lead to memory leaks and a SO threads here and here say that NSInvokation might be better

Bypass SB Dynamically-Generated Methods

This lower level alternative works, but completely misses much of the convenience of SB:

currentTrack := iTunes propertyWithCode: 'pTrk' asObjCFourCharCode.
"Current track can also be retrieved this way, but there doesn't seem to be an advantage...
trackClass := iTunes classForScriptingClass: 'track' asNSString autorelease.
track := iTunes propertyWithClass: trackClass code: 'pTrk' asObjCFourCharCode."
trackName := currentTrack propertyWithCode: 'pnam' asObjCFourCharCode.
trackName get cString

For a command (as opposed to a property as above), a similar approach from SO would be:

iTunes sendEvent:'hook' asObjCFourCharCode id:'PlPs' asObjCFourCharCode parameters:0.
iTunes currentTrack

Directions for further research

Notes

To create application specific scripting definition: sdef /path/to/application.app

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions