How to pass an NSDictionary from x-code as a Record parameter to an AppleScript?

Having defined an AppleScript with a method

	on stringMethod(theString)
		display alert theString
	end stringMethod

then in x-code, I am following Technical Note TN2084
to load the script:

NSAppleScript* appleScript =
                [[NSAppleScript alloc] initWithContentsOfURL:url error:&errors];

The code below from TN2084 shows how to pass the string parameter:

            // create the first parameter
            NSAppleEventDescriptor* firstParameter =
                    [NSAppleEventDescriptor descriptorWithString:@"Message from my app."];

            // create and populate the list of parameters (in our case just one)
            NSAppleEventDescriptor* parameters = [NSAppleEventDescriptor listDescriptor];
            [parameters insertDescriptor:firstParameter atIndex:1];


and then fix things up to finally run the AppleScript method to display the string:
// call the event in AppleScript
if (![appleScript executeAppleEvent:event error:&errors]);
// report any errors from ‘errors’

            [appleScript release];

My question is, how would I change this to support passing an NSDictionary as the single parameter so I can something like:

 NSDictionary *testDict = @{@"FirstName": @"Joe", @"LastName": @"Smith", @"Age": @27};

into an AppleScript like:

	on recordMethod(theRecord)
		display alert FirstName of theRecord
		display alert (Age of theRecord as text)
	end recordMethod

There is no analogous method 'descriptorWithDictionary: ’ for a dictionary parameter initialization. Evidently, you have to use something like:
NSAppleEventDescriptor descriptor

and then fill in the contents of the NSAppleEventDescriptor based on the fields of the NSDictionary.

So this is where I am stuck. I am assuming that if I can build ‘firstParameter’ correctly to mirror the NSDictionary,
then the code that follows will just work, even though the parameter must be structured in AppleScript as a Record instead of the example code using a String.

Thanks in advance for any hints.

Here’s some code that should give you the idea of how you build a record descriptor:

    NSDictionary *testDict = @{@"FirstName": @"Joe", @"LastName": @"Smith"};
    NSAppleEventDescriptor *recDesc = [NSAppleEventDescriptor recordDescriptor];
    NSAppleEventDescriptor *userProperties = [NSAppleEventDescriptor listDescriptor];
    for (id key in testDict) {
        NSAppleEventDescriptor *keyDesc = [NSAppleEventDescriptor descriptorWithString:key];
        [userProperties insertDescriptor:keyDesc atIndex:0]; // index 0 = 'append'
        NSAppleEventDescriptor *valueDesc = [NSAppleEventDescriptor descriptorWithString:testDict[key]];
        [userProperties insertDescriptor:valueDesc atIndex:0];
    [recDesc setDescriptor:userProperties forKeyword:keyASUserRecordFields];

In practice, you would use a handler to build the descriptors for the items in the record, in which you would check their class and pack the values accordingly (I’ve simplified it above by sticking to strings). If you hunt around the Web, you should be able to find pack/unpack code. Hamish Sanderson’s appscript has some that’s probably easy to adapt to your purposes.

(I’ve changed the tag of this thread to a more appropriate section.)

The descriptorWithDescriptorType:data: method is meant for building custom descriptors containing a blob of raw data. There’s no shortcut to creating collection descriptors: you have to pack each item before you add it to the collection descriptor.


Thanks for the suggestions and the sample code packing an NSDictionary.

I got lost trying figure out how to send the result over to AppleScript and then how to unpack it. My immediate need is just for strings, so I decided to push the NSDictionary into a ‘dictionary description’ so then I could use the code from TN2084 to pass the single string parameter over to my function in a script file.

On the objective-c side, it goes like this:

NSDictionary *testDict = @{@"FirstName": @"Joe", @"LastName":@"Smith"};
NSString *plistString = [testDict description];
[self callAppleScript:@"myScriptFileInResourceFolder"
function:@"functionUsingDictionaryParameter" passing:plistString]; //based on TN2084

On the AppleScript side:

on functionUsingDictionaryParameter(plistString)
	current application's NSLog("The plistString is %@", plistString)
	set theDictionary to plistDescriptionToNSDictionary(plistString)
	set theKeyValue to objectForKey_("FirstName") of theDictionary as text
	if theKeyValue is not "missing Value" then
		if theKeyValue is "Joe" then

property NSPropertyListSerialization : a reference to current application's NSPropertyListSerialization
on plistDescriptionToNSDictionary(plistString)
	set theString to stringWithString_(plistString) of NSString of current application
	set theData to theString's dataUsingEncoding:4 -- NSUTF8StringEncoding	
	set theDictionary to NSPropertyListSerialization's propertyListWithData:theData options:0 format:(null) |error|:(null)
	return theDictionary
end plistDescriptionToNSDictionary

FYI, the earlier code would have passed an AppleScript record, which you could convert to a dictionary with:

set theDictionary to current application's NSDictionary's dictionaryWithDictionary:theArg

Thanks again - I got diverted to another project for a while but now have coded up your suggestion to just modify the TN2084 example somewhat to take the recDesc in your code above as parameter instead of the string in TN2048. Works well. Basically simplifies the Applescript side- avoiding my:

property NSPropertyListSerialization : a reference to current application's NSPropertyListSerialization
on plistDescriptionToNSDictionary(plistString) ....

at the expense of more steps on the objective-c side. (ie your loop to construct recDesc instead of my use of [testDict description].)

Not sure if there is any efficiency or other reason to prefer one method over the other? Can’t help wishing for a simple built-in syntax to push parameters back and forth.

It’s not clear where you’re loading the AppleScript file from. Within the Objective-C app’s bundle?