您可以从 Swift 应用程序执行 Applescript 脚本吗

Posted

技术标签:

【中文标题】您可以从 Swift 应用程序执行 Applescript 脚本吗【英文标题】:Can you execute an Applescript script from a Swift Application 【发布时间】:2014-08-06 14:45:19 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

作为Kamaros suggests,您可以直接调用NSApplescript,而无需通过NSTask(如CRGreen suggests)启动单独的进程。

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)")
    

【讨论】:

是否可以从swift中添加参数? @OusmaneTraore 是的,只需在定义myAppleScript 时使用字符串插值。例如,如果你想在 AppleScript 中使用视图的宽度,你可以使用let myAppleScript = "set view_width to \(view.width)" 有没有不用字符串插值的方式添加参数?我想提供一个已定义的参数,然后运行任何符合该 API 的任意 AppleScript。 @zekel 得到“无权将 Apple 事件发送到 Microsoft Excel。”在尝试您的代码时。【参考方案2】:

经过测试:可以做这样的事情(添加了任意脚本路径):

import Foundation
 
let task = Process()
task.launchPath = "/usr/bin/osascript"
task.arguments = ["~/Desktop/testscript.scpt"]
 
task.launch()

【讨论】:

在 Xcode 中禁用“App Sandbox”后,这对我有用。 @meMadhav 与所有沙盒应用程序一样,您需要获得用户的许可才能读取其文件。如果要读取桌面上的文件,可以使用桌面预定义的“读/写”权限。如果你想要一个任意文件夹,你应该让用户从打开的对话框中选择它,如果它是一个 GUI 应用程序,或者如果它是一个 CLI 应用程序,让他们将它作为参数发送到命令行。 对我来说,它只适用于脚本的绝对路径。【参考方案3】:

对于收到以下 Swift 4 警告的任何人,对于从 zekel's answer 创建 NSAppleEventDescriptor 时的行

用于检查可选项的“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 msg 不是预期的复杂类型 在 getMemoryReference 中。

显然,这是 Apple 员工开发人员宣布的错误,据说“只是”一个无害的日志垃圾邮件,并且计划在未来的操作系统中删除更新,您可以在下面这个很长的apple developer forum post 和 SO 问题中看到:

AppleEvents: received mach msg which wasn't complex type as expected in getMemoryReference

感谢 Apple,为那些随处可见的大量垃圾控制台日志。

【讨论】:

对我不起作用。我试图用say "Hello", but I got an error error 运行一个简单的脚本: NSAppleScriptErrorBriefMessage = "A \U201c/\U201d can\U2019t go here."; NSAppleScriptErrorMessage = "A \U201c/\U201d 可以\U2019t 到这里。"; NSAppleScriptErrorNumber = "-2740"; NSAppleScriptErrorRange = "NSRange: 0, 1"; `【参考方案4】:

您可以尝试来自 Apple 技术说明 TN2084 的 NSAppleScript 在 Cocoa 应用程序中使用 AppleScript 脚本 https://developer.apple.com/library/mac/technotes/tn2084/_index.html

NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
            @"\
            set app_path to path to me\n\
            tell application \"System Events\"\n\
            if \"AddLoginItem\" is not in (name of every login item) then\n\
            make login item at end with properties hidden:false, path:app_path\n\
            end if\n\
            end tell"];

returnDescriptor = [scriptObject executeAndReturnError: &errorDict];

【讨论】:

这不是Objective-C吗? OP 要求采用 Swift 方式。【参考方案5】:

我挣扎了几个小时,但没有任何效果。最后我设法通过 shell 运行 AppleScript:

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

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

【讨论】:

脚本错误 -54。由于错误 -1700,无法获取错误文本。 为什么使用env 作为shell 而不是bashzsh 经过巨大的努力,这对我有所帮助。谢谢。【参考方案6】:

截至 2018 年 3 月,我认为此线程上最强的答案仍然是 accepted answer from 2011。涉及使用 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()
    

【讨论】:

以上是关于您可以从 Swift 应用程序执行 Applescript 脚本吗的主要内容,如果未能解决你的问题,请参考以下文章

从 Swift 中的其他类更改 ViewController 的对象

在 Swift 中从字符串调用方法

通过 Swift 关闭推送通知功能

如何在Swift中检查URL的有效性?

获取“无法打开应用程序,因为您无权查看它。 “将项目代码从swift 2.2转换为swift 3.0之后

Swift 3 从 Firebase 获取用户数据