0
votes

I am on macOSX (Sierra), not iOS, Objective-C, Xcode9.

I have an application with implemented applescript classes, XCode compiles them for me. Whenever i call an applescript method from within an *.m file (and from a background thread!) i dispatch_sync it to the main thread (as applescript needs to be executed in the main thread) - sync cause i need the result to continue.

I'd like to show you with a simple example:

Applescript:

on openFile_filePath(filePath)
    try
        tell application "Adobe InDesign CC 2018"
            set myDoc to open (filePath as string)
            return id of myDoc
         end tell
    end    
    return 0
end

Objective-C

// Method is running in a background thread
// appleScriptHelper is properly instantinated
__block NSInteger docID = 0; 
NSString* someFile = @"/Users/user/Desktop/";
dispatch_sync(dispatch_get_main_queue(), ^{
    docID = [self.appleScriptHelper openFile:someFile];
});

This runs smooth - as long as InDesign responds! Sometimes, InDesign freezes and then my whole app freezes cause it basically waits forever on the main thread.

What i tried: I cannot use a timer cause the main thread is blocked anyways and i cant pass a "cancel" message - cause Applescript is busy anyways waiting for something never happen. In addition it is not possible to work with "with timeout of x seconds" in applescript when compiled from XCode (just doesnt work as mentioned in other posts). I tried it with NSOperations but as i need to dispatch to the mainThread... still the same issue.

So my question is: Is there any way i can STOP the whole dispatch_sync block after a time of x seconds? Or is there any other possibility to keep the app running and not being locked up forever through InDesign freeze?

Note: The implementation works fine - please don't recommend 'use NSApplescript' or 'use Scripting Bridge'. Reasons: ScriptingBridge - it's almost impossible to create a working HEADER file for InDesign (it almost weights 15 MB and throws tons of compiler errors - i did fix that once with lot of manual work just to see it not working anymore for the next Version of InDesign). NSApplescript is fine as long as you have simple scripts which is not the case.

Any help appreciated

1

1 Answers

0
votes

Were NSAppleScript sufficient for your needs, I'd say use NSUserAppleScriptTask which runs scripts out of process with async completion callbacks. And yes, as you say, Scripting Bridge is utterly unfit for non-trivial automation, especially any involving large complex crusty Carbon apps like Adobe's, so don't waste a second on that.

There is AppleEventBridge/SwiftAE, but with Apple shuffling off the whole AS/AE infrastructure to quietly die off, I don't promote or support those any more, so maxima caveat emptor. (I still use Python appscript for my own Adobe app automation,btw, and it continues to blow everything else out the water, but I won't be surprised if the whole industry eventually goes Windows as the only professional-oriented platform left.)

..

The problem with using AppleScript-ObjC* is that AppleScript component (aka interpreter) instances are not thread-safe: you can instantiate them on any thread you like, but you can only use them on that thread, not from others. ASOC doesn't let you control any of this stuff for yourself; neither does NSAppleScript. (OSAKit does, but it's as painful to use as NSAppleScript is.) So ASOC code is de facto limited to running on the main thread only.

If you're stuck with using ASOC, I think your best bet is to push that code out into a subprocess that your main process talks to asynchronously via XPC Services or whatever. That'll avoid blocking your main process's main event loop (which then blocks its GUI) while allowing ASOC to do its own thing independently.


  • p.s. Another problem with ASOC is that changes in 10.13's bridgesupport files have changed/broken how C APIs are mapped to AS, which can break existing ASOC-based scripts. (I've stopped recommending ASOC unless/until that's fixed.)