4

I'm experimenting with Scripting Bridge for the the first time, but have run into an issue with filtering a SBElementArray according to a NSPredicate containing a FourCharCode enum constant as a criterion.

I wrote a trivial program to identify the "library" source in a user's iTunes library, by using -filteredArrayUsingPredicate: to filter the SBElementArray of all iTunes sources. I was expecting to get back an SBElementArray that, when evaluated, would produce an array of one element, namely the library source. Instead, when I call -get on the returned SBElementArray, I get back an empty array.

Perplexingly, if change the order and instead call -get on the SBElementArray of all sources to get a concrete NSArray, and call -filteredArrayUsingPredicate: on this array with the same predicate as before, I do get the desired result. I don't believe this is supposed to be necessary however, and I've had success filtering a SBElementArray using other NSPredicates (e.g. @"name=='Library'" works fine).

The code snippet is below. iTunesESrcLibrary is a FourCharCode constant defined in the header file generated by Scripting Bridge. (iTunesESrcLibrary = 'kLib'). I'm running 10.6.5.

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

NSPredicate* libraryPredicate = [NSPredicate predicateWithFormat:@"kind == %u", iTunesESrcLibrary];

SBElementArray* allSources_Attempt1 = [iTunes sources];
SBElementArray* allLibrarySources_Attempt1 = (SBElementArray*)[allSources_Attempt1 filteredArrayUsingPredicate:libraryPredicate];

NSLog(@"Attempt 1: %@", allLibrarySources_Attempt1);
NSLog(@"Attempt 1 (evaluated): %@", [allLibrarySources_Attempt1 get]);


NSArray* allSources_Attempt2 = [[iTunes sources] get];
NSArray* allLibrarySources_Attempt2 = [allSources_Attempt2 filteredArrayUsingPredicate:libraryPredicate];

NSLog(@"Attempt 2: %@", allLibrarySources_Attempt2);

The output I get is the following:

Attempt 1: <SBElementArray @0x3091010: ITunesSource whose 'cmpd'{ 'relo':'=   ', 'obj1':'obj '{ 'want':'prop', 'from':'exmn'($$), 'form':'prop', 'seld':'pKnd' }, 'obj2':1800169826 } of application "iTunes" (88827)>
Attempt 1 (evaluated): (
)
Attempt 2: (
"<ITunesSource @0x3091f10: ITunesSource id 65 of application \"iTunes\" (88827)>"
)
Nick Hutchinson
  • 5,044
  • 5
  • 33
  • 34

1 Answers1

5

I think I've figured it out. It seems you can't simply use a FourCharCode's integer value directly in a NSPredicate that you intend to use to filter an SBElementArray.

By chance, I found that instead of:

[NSPredicate predicateWithFormat:@"kind == %u", iTunesESrcLibrary]

you need to use:

[NSPredicate predicateWithFormat:@"kind == %@", [NSAppleEventDescriptor descriptorWithTypeCode: iTunesESrcLibrary]]

Using this second form, I can filter the SBElementArray sources list as expected. However, this new predicate can't be used to filter the NSArray, even though this array is just the evaluated form of the SBElementArray! Here, you have to switch back to the %u version.

Rant:
Frankly this sucks, and it seems the sort of thing Scripting Bridge should deal with so I don't have to; I shouldn't have to know what an NSAppleEventDescriptor is. And while it's reasonable that not all predicates that work with NSArray should work with SBElementArray, the converse should not be the case and it's unnecessarily confusing that it is.

Nick Hutchinson
  • 5,044
  • 5
  • 33
  • 34
  • I feel your pain. I'm digging into Scripting Bridge as well for a bunch of internal production work and I've had to come up with a couple wrapper classes to make SB easier to use. The work is worth it just for the Cocoa integration alone, but there are a lot of hoops to jump through to get started. – Philip Regan Jan 04 '11 at 14:38