0

I'm building an application for macOS and for some of it's functionality I rely on calling AppleScript:

let appleScript = NSAppleScript(source: theScriptIWantToExecute)
var errorDict: NSDictionary? = nil
let possibleResult = appleScript?.executeAndReturnError(&errorDict)

Now I've discovered that some scripts in some circumstances on some applications when they fail they rather than throw an error they crash the whole application. While it's bad that the script fails it's not so critical that the whole UI should crash.

My idea was to separate out the "Apple Script Execution" part into a completely separate process that will be called by the main application. In case it crashes, it can simply be restarted without any consequence for the main app.

I've been thinking what would be the best solution. Since it's more or less a functional problem I've been tempted to use a command line tool, but command line tools only return text if I'm not mistaken. I would rather return some objects. But I don't really need a service.

XPC seems to support this but is geared more towards services.

What is the best way to isolate my main app from these crashes while still being able to use high level objects?


For those interested in how I fixed it:

  • Create a separate XPC project (the template code is pretty fine to start off with)
  • Create a separate project with all of the types you want to share "across the bridge" and import it in both targets

If you don't share the types you're going to have a lot of confusing build- and run-time problems.

Lucas van Dongen
  • 9,328
  • 7
  • 39
  • 60
  • 1
    Is the crash an Objective-C exception? – Willeke May 20 '22 at 09:40
  • 1
    It's unusual for an AppleScript to crash ***that*** badly, so it makes me curious what kinds of things you're trying to do with it. Can you spell that out a little more please? I"m tentatively thinking that you might want to reconstruct your script as an AppleScript app (you could launch it from your main app's bundle and let it run as a separate process), but I don't know it that's feasible for your purposes. – Ted Wrigley May 21 '22 at 22:29
  • @Willeke I think it was a segfault – Lucas van Dongen Jul 12 '22 at 14:10
  • @TedWrigley I think AppleScript support needs to be built by the vendor. If they do anything weird there they can probably crash it. It was Illustrator by the way, it crashed my script when it was starting the app. – Lucas van Dongen Jul 12 '22 at 14:10

1 Answers1

1

You can do this using an XPC service. In fact "stability" is one of the two key reasons Apple created XPC services. So if this instability is unavoidable, then creating an XPC service is a good way to go about doing this.

Apple provides two main ways to communicate with an XPC service: their XPC C framework and their Objective-C framework which is often referred to as the NSXPCConnection API. While both can be used from Swift with its built in language bridging functionality, neither are particularly pleasant to use from Swift. I've created SecureXPC which is written from the ground up for Swift and I think is quite a bit easier and nicer to use.

Joshua Kaplan
  • 843
  • 5
  • 9
  • I ended up using it an Objective-C XPC service that indeed did isolate the crash. One of the most complex tasks was getting the types "across the bridge" until I added a separate framework imported by both targets that had the shared data objects. SecureXPC looks nice. Does it support Swift type enums with associated data? – Lucas van Dongen Jul 11 '22 at 13:51
  • 1
    Yes, so long as everything is `Codable` including the associated data. Considering the Swift compiler can autogenerate conformance to `Codable` for many value types, this often requires very minimal work. If you have any other questions or thoughts, I'd be quite appreciative to get your feedback via you starting up a new a [Github discussion](https://github.com/trilemma-dev/SecureXPC/discussions). – Joshua Kaplan Jul 12 '22 at 12:42