Reputation: 357
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?
Upvotes: 1
Views: 1295
Reputation: 496
how did you get past the sandboxing? Whatever I try I keep getting something like:
Script execution error: The file “create.scpt” couldn’t be opened because you don’t have permission to view it.
When sandboxing is turned of or the script is executed manually everything works
entitlements:
com.apple.security.temporary-exception.apple-events
com.apple.systemevents
com.apple.applescript
TY.
Edit: It seems that if i put the script into ~/Library/Application Scripts/com.myapp/ and execute that script everything works fine
Upvotes: 0
Reputation: 539975
(Motivated by omz's comment above I'm writing this as an answer.)
You could use the completion handler of the task to start execution of the next task.
Upvotes: 1
Reputation: 53561
You could run the scripts from a dispatch queue (or operation queue) and use an NSConditionLock
to wait for each script to complete.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];
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]);
}
[lock lock];
[lock unlockWithCondition:1];
}];
[task release];
//This will wait until the completion handler of the script task has run:
[lock lockWhenCondition:1];
[lock unlockWithCondition:0];
}
[lock release];
});
Upvotes: 2