Search code examples
swiftapplescript

Swift: my application crashes when I try to return the result of an AppleScript


I have an AppleScript checking if the number of windows of an application is under 1 (there is one or more, or not). If an application has no windows open, the script returns its Bundle ID. So far, everything is fine.

I tried to integrate it into a function in my Swift app, which should return the result of the script as a String. Xcode doesn't tell me any problem, but as soon as my app uses the function, it crashes...

I think the problem is related to the unwrap of the script result, but I'm not sure.

Here's my code:

func checkApp(appBundleID: String) -> String {
     let appleScript = """
     set bundleID to "\(appBundleID)"
     tell application id bundleID
         set windowsCount to (count of windows)
     end tell
     if windowsCount < 1 then
         return bundleID
     end if
     """

     var output = "" 
     if let script = NSAppleScript(source: appleScript) {
         var error: NSDictionary?
         output = script.executeAndReturnError(&error).stringValue!
     }
     return output
}

Solution

  • Your script returns the result of the last AppleScript line, which is a null AppleEventDescriptor if the application has one or more windows. This cannot be coerced to string and causes the crash.

    My suggestion is to return an optional string in case of an error, for example if the target application is not scriptable, and to return the number of windows coerced to string if there is at least one window.

    You could even check error and return something from the dictionary or create a custom error and return a Result<String,Error> type

    func checkApp(appBundleID: String) -> String? {
        let appleScript = """
         set bundleID to "\(appBundleID)"
         tell application id bundleID
             set windowsCount to (count of windows)
         end tell
         if windowsCount < 1 then
             return bundleID
         else
            return windowsCount as string
         end if
         """
        
        var output : String?
        if let script = NSAppleScript(source: appleScript) {
            var error: NSDictionary?
            output = script.executeAndReturnError(&error).stringValue
        }
        return output
    }
    

    Edit: This is a simpler version. It returns the number of windows or -1 if the application is not scriptable.

    As the hard-coded script is error-free and the AppleScript error is caught in the script NSAppleScript(source: appleScript) can be force unwrapped and the error parameter can be set to nil.

    func checkApp(appBundleID: String) -> Int {
        let appleScript = """
         set bundleID to "\(appBundleID)"
         try
            tell application id bundleID
                return (count of windows)
            end tell
         on error
            return -1
         end try
         """
        
        let script = NSAppleScript(source: appleScript)!
        return Int(script.executeAndReturnError(nil).int32Value)
    }