4

In order to support binary data exchange in my scriptable Mac app, I like to make it possible to receive and deliver data as NSData, using the AS-ObjC bridge, if that's possible.

For instance, I like to make this code possible in AppleScript:

use framework "Foundation"

set theData to current application's NSData's dataWithContentsOfFile:"/some/binary/file"

tell application "MyApp"
    set raw value to theData
end tell

The sdef contains a value-type and property for this:

<suite name="My Suite" code="Demo">
    <value-type name="ObjCNSData" code="NSDa">
        <cocoa class="NSData"/>
    </value-type>
    <class name="application" code="capp">
        <property name="raw data" code="rawD" type="ObjCNSData">
            <cocoa key="rawData"/>
        </property>

I then implement the conversion handler as an extension to NSData, similarly to how the Sketch example converts NSColor to the value-type "RGB Color":

@implementation NSData(DemoScripting)
+ (NSData *)scriptingObjCNSDataWithDescriptor:(NSAppleEventDescriptor *)desc {
    id res = [desc coerceToDescriptorType:'NSDa'];
    // -> res is NULL, which is not getting me any further
}

The desc's description is:

<NSAppleEventDescriptor: 'obj '{
  'form':'ID  ',
  'want':'ocid',
  'seld':'optr'($E0A8430080600000$),
  'from':null()
}>

Similarly, invoking [NSScriptObjectSpecifier _scriptingSpecifierWithDescriptor:descriptor] returns NULL as well.

So, how do I get to the actual NSData object inside my app code?

And how do I return a NSData object to the AppleScript?

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149
  • `NSData` and AppleScript `data` are not interchangeable. But the type of the `with data` parameter is not necessarily binary data. You could add `value-type` entries in your sdef file to convert `NSData` to the AS type `MyAppType` expects. In your code `theData` represents clearly UTF8 encoded String. The `NSAppleEventDescriptor` returned from the `make` command contains a pointer (`'optr'`) to the `NSData` instance. I'd recommend to ask the ASOC gurus Mark Alldritt from LateNightSoftware or Shane Stanley directly – vadian Apr 26 '19 at 12:42
  • So you mean I should evaludate the "optr" myself instead of looking for a framework function that'd do that for me? I can do that. But that still leaves the question of how to return a NSData object. – Thomas Tempelmann Apr 26 '19 at 14:04

1 Answers1

1

Shane Stanley did indeed know a way, and it does not even require extra code in my app - instead, it can all be done in AppleScript, with these two conversion functions:

use framework "Foundation"

set nsData1 to current application's NSData's dataWithContentsOfFile:"/etc/hosts"
set asData to my ASDataFromNSData(nsData1)
set nsData2 to my NSDataFromASData(asData)

on ASDataFromNSData(theData)
    set theCode to current application's NSHFSTypeCodeFromFileType("'rdat'")
    return (current application's NSAppleEventDescriptor's descriptorWithDescriptorType:theCode |data|:theData) as data
end ASDataFromNSData

on NSDataFromASData(asData)
    return (current application's NSArray's arrayWithObject:asData)'s firstObject()'s |data|()
end NSDataFromASData

It appears that rdat is a special AppleScript type for this purpose, with the framework automatically handling the conversion with NSData. I can't find that type declared in the AE.framework's headers, though.

I then still have to handle this rdat type explicitly in my app's code, though. But I won't need the value-type in the sdef, and can change the property to:

<property name="raw data" code="rawD" type="any">
    <cocoa key="rawData"/>
</property>

Returning data as rdat is similar. My -rawData method:

return [NSAppleEventDescriptor descriptorWithDescriptorType:'rdat' data:myNSData];

This only works if I declare the property type as "any", though. If I use type="rdat", Script Debugger shows the type as a dedicated raw data type, but then I get -10000 errors when trying to set or get the property in a script.

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149
  • 1
    There should be more logging in Xcode that may help you identify the cause of the problem. If there isn't, enable Cocoa Scripting debug logging. There is probably some attempt at conversion that is failing and you'll need to implement a method for that. – Mark Alldritt Apr 26 '19 at 17:14
  • You can mark your own answer as the selected/"correct" answer, as it'll be useful for other users too (like myself). – CJK Apr 26 '19 at 22:57
  • 1
    @CJK Remind me tomorrow. SO let's me accept my own answers only after 2 days, so that others still have a chance to provide a better one. And then delete your comment (as I will mine) to reduce clutter :) – Thomas Tempelmann Apr 27 '19 at 11:26
  • Thomas & CJK, tomorrow has come and gone! :-) – Ron Reuter Feb 29 '20 at 14:26