能否从Swift应用程序执行Applescript脚本

能否从Swift应用程序执行Applescript脚本,swift,applescript,Swift,Applescript,我有一个简单的AppleScript发送电子邮件。我如何在Swift应用程序中调用它 (我无法通过谷歌找到答案。)你可以试试苹果技术说明TN2084中的NSAppleScript 在Cocoa应用程序中使用AppleScript脚本 已测试:可以执行以下操作(添加了任意脚本路径): 作为,您可以直接调用,而无需通过NSTask(As)启动单独的进程 Swift代码 let myAppleScript = "..." var error: NSDictionary? if let scriptO

我有一个简单的AppleScript发送电子邮件。我如何在Swift应用程序中调用它


(我无法通过谷歌找到答案。)

你可以试试苹果技术说明TN2084中的NSAppleScript 在Cocoa应用程序中使用AppleScript脚本


已测试:可以执行以下操作(添加了任意脚本路径):

作为,您可以直接调用,而无需通过NSTask(As)启动单独的进程

Swift代码

let myAppleScript = "..."
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
    if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(
                                                                       &error) {
        print(output.stringValue)
    } else if (error != nil) {
        print("error: \(error)")
    }
}

我挣扎了几个小时,但什么也没起作用。最后,我设法通过shell运行AppleScript:

let proc = Process()
proc.launchPath = "/usr/bin/env"
proc.arguments = ["/usr/bin/osascript", "scriptPath"]
proc.launch()

不知道这是最好的方法,但至少它是有效的。

截至2018年3月,我认为这方面最有力的答案仍然是。使用NSAppleScript或OSAScript的实现遇到了一些缺点,即存在一些轻微但非常令人不快的内存泄漏,但实际上没有提供任何额外的好处。任何试图正确执行该答案(在Swift 4中)的人都可能希望尝试以下方法:

let manager = FileManager()
// Note that this assumes your .scpt file is located somewhere in the Documents directory
let script: URL? = try? manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if let scriptPath = script?.appendingPathComponent("/path/to/scriptName").appendingPathExtension("scpt").path {
    let process = Process()
    if process.isRunning == false {
        let pipe = Pipe()
        process.launchPath = "/usr/bin/osascript"
        process.arguments = [scriptPath]
        process.standardError = pipe
        process.launch()
    }
}

对于在创建NSAppleEventDescriptor时收到以下关于Swift 4的警告的任何人

用于检查可选项的“NSAppleEventDescriptor”类型的非可选表达式

您可以使用此编辑的简短版本将其删除:

let myAppleScript = "..."
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
    if let outputString = scriptObject.executeAndReturnError(&error).stringValue {
        print(outputString)
    } else if (error != nil) {
        print("error: ", error!)
    }
}
然而,您可能也意识到;使用此方法,每次运行脚本时,系统都会将此消息记录到控制台:

AppleEvents:收到mach消息,该消息不是预期的复杂类型 在getMemoryReference中

显然,这是一个由苹果员工开发人员宣布的bug,据说“只是”一个无害的日志垃圾邮件,计划在未来的操作系统更新中删除,正如您在下面这个非常长的问题中看到的那样:


感谢苹果公司,因为它扔掉了数以百万计的垃圾控制台日志。

可以从swift中添加参数吗?@ousmanetrao是的,只要在定义
myAppleScript
时使用字符串插值即可。例如,如果您想在AppleScript内部使用视图的宽度,您可以执行
让myAppleScript=“将视图宽度设置为\(view.width)”
是否有方法添加参数而无需字符串插值?我想提供一个已定义的参数,然后运行任何符合该API的任意AppleScript。@zekel在尝试代码时获得“未授权将Apple事件发送到Microsoft Excel”。这不是Objective-C吗?OP要求提供快捷方式。脚本错误-54。由于错误-1700,无法获取错误文本。为什么使用
env
作为shell而不是
bash
zsh
?经过巨大的努力,这对我很有帮助。谢谢。在禁用Xcode中的“应用程序沙盒”后,这对我起到了作用。@meMadhav与所有沙盒应用程序一样,您需要获得用户的权限才能读取其文件。如果要读取桌面上的文件,可以使用桌面的预定义“读/写”权限。如果您想要一个任意文件夹,您应该让用户从“打开”对话框中选择它(如果它是GUI应用程序),或者如果它是CLI应用程序,则让他们在命令行上将其作为参数发送。对我来说,它只适用于脚本的绝对路径。对我来说不适用。我试图运行一个简单的脚本,用
说“你好”,但我得到了一个错误:{NSAppleScriptErrorBriefMessage=“a\U201c/\U201d can\U2019t转到这里。”NSAppleScriptErrorMessage=“a\U201c/\U201d can\U2019t转到这里。“NSAppleScriptErrorNumber=“-2740”;NSAppleScriptErrorRange=“NSRange:{0,1}”`
let manager = FileManager()
// Note that this assumes your .scpt file is located somewhere in the Documents directory
let script: URL? = try? manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if let scriptPath = script?.appendingPathComponent("/path/to/scriptName").appendingPathExtension("scpt").path {
    let process = Process()
    if process.isRunning == false {
        let pipe = Pipe()
        process.launchPath = "/usr/bin/osascript"
        process.arguments = [scriptPath]
        process.standardError = pipe
        process.launch()
    }
}
let myAppleScript = "..."
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
    if let outputString = scriptObject.executeAndReturnError(&error).stringValue {
        print(outputString)
    } else if (error != nil) {
        print("error: ", error!)
    }
}