I am migrating some code that executes AppleScripts from NSAppleScript to NSUserAppleScriptTask so that I can sandbox my app. The problem I am having can be best demonstrated as follows:
The AppleScript “test.scpt” is simply
on run
display dialog "Hello World" buttons {"OK"} default button "OK"
end run
If I execute this 10 times in a row with NSAppleScript as below, the script is executed 10 times, with each execution waiting for the previous execution to complete.
NSURL *script = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"scpt"]];
for (int i=0; i<10; i++) {
NSDictionary *error = nil;
NSAppleScript *task = [[NSAppleScript alloc] initWithContentsOfURL:script error:nil];
[task executeAndReturnError:&error];
if (error!=nil) {
NSLog(@"AppleScript error: %@", error);
}
[task release];
}
However using NSUserAppleScriptTask it appears the tasks are executed concurrently. The concurrent executions is a “problem” as it seems that if a previous script has a dialog open, the next script to be executed errors. This can be demonstrated as below:
NSURL *script = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"scpt"]];
for (int i=0; i<10; i++) {
NSError *error;
NSUserAppleScriptTask *task = [[NSUserAppleScriptTask alloc] initWithURL:script error:&error];
[task executeWithCompletionHandler:^(NSError *error) {
if (error){
NSLog(@"Script execution failed with error: %@", [error localizedDescription]);
}
}];
[task release];
}
This generates the following error for 9 out of the 10 executions:
execution error: "Hello World" doesn’t understand the «event sysodlog» message. (-1708)
I thought the correct solution would be to queue each operation using gcd or NSOperationQueue, but I’ve not managed to construct a queue that waits for the completion block of the NSUserAppleScriptTask to be executed before it starts the next task.
Can anyone suggest a solution that would give me the same behaviour as the NSAppleScript method gives me?
You could run the scripts from a dispatch queue (or operation queue) and use an
NSConditionLockto wait for each script to complete.