title: NSUserScriptTask difficulties
body: <p>I've been trying to make do (see <a href="https://stackoverflow.com/questions/15599493/nsuserscripttask-cancelling-tasks">this</a> and <a href="https://stackoverflow.com/questions/15603062/nsuserscripttask-accepted-file-types">this</a>) with the recent NSUserScriptTask class and its subclasses and so far I've solved some problems, but some others remain to be solved. As you can see from the <a href="http://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSUserScriptTask_Class/Reference/Reference.html" rel="nofollow noreferrer">docs</a>, NSUserScriptTask does not allow for the cancellation of tasks. So, I decided to create a simple executable that takes as arguments the path to the script and runs the script. That way, I can launch the helper from my main app using NSTask and call <code>[task terminate]</code> when necessary. However, I require:</p>
  
  <ul>
  <li>The main app to receive output and errors from the helper it launched</li>
  <li>The helper only terminating when the NSUserScriptTask is done</li>
  </ul>
  
  <p>The code for the main app is simple: just launch an NSTask with the proper info. Here's what I have now (for the sake of simplicity I ignored the code for security-scoped bookmarks and the like, which are out of the problem. But don't forget this is running sandboxed):</p>
  
  <pre><code>// Create task
  task = [NSTask new];
  [task setLaunchPath: [[NSBundle mainBundle] pathForResource: @"ScriptHelper" ofType: @""]];
  [task setArguments: [NSArray arrayWithObjects: scriptPath, nil]];
  
  // Create error pipe
  NSPipe* errorPipe = [NSPipe new];
  [task setStandardError: errorPipe];
  
  // Create output pipe
  NSPipe* outputPipe = [NSPipe new];
  [task setStandardOutput: outputPipe];
  
  // Set termination handler
  [task setTerminationHandler: ^(NSTask* task){        
      // Save output
      NSFileHandle* outFile = [outputPipe fileHandleForReading];
      NSString* output = [[NSString alloc] initWithData: [outFile readDataToEndOfFile] encoding: NSUTF8StringEncoding];
  
      if ([output length]) {
          [output writeToFile: outputPath atomically: NO encoding: NSUTF8StringEncoding error: nil];
      }
  
      // Log errors
      NSFileHandle* errFile = [errorPipe fileHandleForReading];
      NSString* error = [[NSString alloc] initWithData: [errFile readDataToEndOfFile] encoding: NSUTF8StringEncoding];
  
      if ([error length]) {
          [error writeToFile: errorPath atomically: NO encoding: NSUTF8StringEncoding error: nil];
      }
  
      // Do some other stuff after the script finished running &lt;-- IMPORTANT!
  }];
  
  // Start task
  [task launch];
  </code></pre>
  
  <p>Remember, I need the termination handler to only run when: (a) the task was cancelled (b) the task terminated on its own because the script finished running.</p>
  
  <p>Now, on the helper side things start to get hairy, at least for me. Let's imagine for the sake of simplicity that the script is an AppleScript file (so I use the NSUserAppleScriptTask subclass - on the real world I'd have to accomodate for the three types of tasks). Here's what I got so far:</p>
  
  <pre><code>int main(int argc, const char * argv[])
  {
      @autoreleasepool {
          NSString* filePath = [NSString stringWithUTF8String: argv[1]];
          __block BOOL done = NO;
  
          NSError* error;
          NSUserAppleScriptTask* task = [[NSUserAppleScriptTask alloc] initWithURL: [NSURL fileURLWithPath: filePath] error: &amp;error];
  
          NSLog(@"Task: %@", task); // Prints: "Task: &lt;NSUserAppleScriptTask: 0x1043001f0&gt;" Everything OK
  
          if (error) {
              NSLog(@"Error creating task: %@", error); // This is not printed
              return 0;
          }
  
          NSLog(@"Starting task");
  
          [task executeWithAppleEvent: nil completionHandler: ^(NSAppleEventDescriptor *result, NSError *error) {
              NSLog(@"Finished task");
  
              if (error) {
                  NSLog(@"Error running task: %@", error);
              }
  
              done = YES;
          }];
  
          // Wait until (done == YES). How??
      }
  
      return 0;
  }
  </code></pre>
  
  <p>Now, I have three questions (which are the ones I want to ask with this SO entry). <strong>Firstly</strong>, "Finished task" never gets printed (the block never gets called) because the task never even starts executing. Instead, I get this on my console:</p>
  
  <pre><code>MessageTracer: msgtracer_vlog_with_keys:377: odd number of keys (domain: com.apple.automation.nsuserscripttask_run, last key: com.apple.message.signature)
  </code></pre>
  
  <p>I tried running the exact same code from the main app and it completes without a fuss (but from the main app I lose the ability to cancel the script).</p>
  
  <p><strong>Secondly</strong>, I only want to reach the end of main (<code>return 0;</code>) after the completion handler is called. But I have no idea how to do that.</p>
  
  <p><strong>Thridly</strong>, whenever there's an error or output from the helper I want to send that error/output back to the app, which will receive them through the errorPipe/outputPipe. Something like <code>fprintf(stderr/stdout, "string")</code> does the trick, but I'm not sure if it is the right way to do it.</p>
  
  <p>So, in short, any help regarding the first and second problems is appreciated. The third one I just want to make sure that's how I'm supposed to do it.</p>
  
  <p>Thanks</p>