The AppleScript duplicate
command is supposed to return the copied objects.
And while apps using the original AE-based functions seem to do that, apps based
Mark Aldritt helped me a little further, telling me about some private API methods:
@interface NSScriptObjectSpecifier (NSPrivate)
+ (id) _scriptingSpecifierWithDescriptor:(NSAppleEventDescriptor*) descriptor;
+ (id) _objectSpecifierFromDescriptor:(NSAppleEventDescriptor*) descriptor inCommandConstructionContext:(id) context;
- (NSAppleEventDescriptor*) _asDescriptor;
@end
The _asDescriptor
was what I was looking for - a way to turn an object specifier into a NSAppleEventDescriptor
so that I can add that to a list object. The code for that would look like this:
- (NSAppleEventDescriptor*) objectSpecifiersAsList:(NSArray*) objectSpecifiers {
NSAppleEventDescriptor* result = [NSAppleEventDescriptor listDescriptor];
for (NSScriptObjectSpecifier* specifier in objectSpecifiersArray) {
[result insertDescriptor:specifier._asDescriptor atIndex:0];
}
return result;
}
When I tried this to return the non-consecutive items, I found, however, that this doesn't work. In fact, it has the same effect as returning an NSArray
of the same NSScriptObjectSpecifier
s. Here's an example:
set x to duplicate widgets 1 thru 2
With the custom duplicate command handler returning a list of specifiers for the copied items 3 and 4, AppleScript ends up calling the same command handler a second time and after that it gives the error -10006 with the message:
Can't set widgets 1 thru 2 to widgets 1 thru 2
Mind you - it does not say "widgets 3 thru 4" or "{widget 3, widget 4}". No, it always reports the items that were given at the first parameter to the duplicate command.
As soon as I change my code to returning a single specifiers or a range specifier, the command behaves normally again.
So it seems like this is a hidden bug in Cocoa Scripting (or AppleScript?) wherein it cannot handle returned object specifiers in a list.
Update & Solution
After more trial-and-error I figured out a way that works:
The type for the result has to be changed from "descriptor", and there are two possibilities:
To use the code above that returns a listDescriptor
, the result type needs to be "any", i.e.:
<result>
<type type="any"/>
</result>
Alternatively, if the result type is changed to "list of any", then one can return an NSArray containing the NSAppleEventDescriptor
values:
<result>
<type type="any" list="yes"/>
</result>
Both solutions require the use of the private _asDescriptor
method, however, as there is no other known way to turn a scriptable object into a NSAppleEventDescriptor
.
(Of course, if your app supports the duplicate
command for only one type of element, then you can change the type to "list of yourtype" and return simply an NSArray of your objects, without the need for the private method - that's only needed for returning results of type any
.)
Mark says this about using the private method:
If you are concerned about Mac App store issues, these private methods were give to me by Apple as there is no alternative API. I’m pretty sure you can get permission to use them.
I hope to submit my own app implementing this solution to the App Store soon. I shall then update this answer with the outcome of using the private function.
Thomas, I'm not sure if it was a typo, but I'm not seeing an "at" parameter for the duplicate command in the Standard Suite:
<command name="duplicate" code="coreclon" description="Copy an object.">
<cocoa class="NSCloneCommand"/>
<direct-parameter type="specifier" .../>
<parameter name="to" ...</parameter>
<parameter name="with properties" ...</parameter>
</command>
There is also no <result ...>
element, so the command should not return any value or values, by definition. Am I missing something?