1

我正在尝试在 Swift 2 中创建一个运行并获取 AppleScript 脚本结果的程序。

这是我的代码:

import Foundation

func runAppleScript(script:String) -> String
{
    let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()
    let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
    let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
    let theResult:String = theDiscriptor.stringValue! //This is whats causing the error

    return theResult
}

let scriptResult = runAppleScript("tell app \"Spotify\" to playpause")

NSLog("\(scriptResult)")

问题是程序崩溃并输出:

致命错误:在展开可选值时意外发现 nil

在控制台中。我也尝试过if let else,但这也不起作用。我将如何解决这个问题?

这是使用 swift 语言使用 OS X 命令行模板进行测试的。

4

2 回答 2

1

实际上错误可能来自NSAppleScript(source: script)!因此正确的解决方案是返回一个可选字符串并且根本不使用强制展开

func runAppleScript(script:String) -> String? {
    let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?> = nil
    let startAtLoginScript = NSAppleScript(source: script)
    let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo)
    return theDescriptor?.stringValue
}

if let scriptResult = runAppleScript("tell app \"Spotify\" to playpause") {
    NSLog("\(scriptResult)")
} else {
    print("the script execution failed")
}

如果您希望在失败时使用默认值而不是 nil,则无需返回 Optional:

func runAppleScript(script:String) -> String {
    let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?> = nil
    let startAtLoginScript = NSAppleScript(source: script)
    let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo)
    return theDescriptor?.stringValue ?? ""  // if nil, returns the default ""
}

let scriptResult = runAppleScript("tell app \"Spotify\" to playpause")
NSLog("\(scriptResult)")

至于使用新的Swift 2错误处理系统,您在内部使用的所有方法都runAppleScript不会抛出错误,因此只有在您使用自定义错误类型并自己抛出错误时才会起作用。例子:

enum MyAppleScriptError: ErrorType {
    case ExecutingScriptFailed
    case GettingStringValueFailed
}

func runAppleScript(script:String) throws -> String {
    let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?> = nil
    let startAtLoginScript = NSAppleScript(source: script)
    guard let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo) else {
        throw MyAppleScriptError.ExecutingScriptFailed
    }
    guard let value = theDescriptor.stringValue else {
        throw MyAppleScriptError.GettingStringValueFailed
    }
    return value
}

do {
    let scriptResult = try runAppleScript("tell app \"Spotify\" to playpause")
    NSLog("\(scriptResult)")
} catch {
    print(error)
}

斯威夫特 3

相同的想法,但一些实现细节不同。

func runAppleScript(_ script:String) -> String? {
    let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?>? = nil
    if let startAtLoginScript = NSAppleScript(source: script) {
        let theDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
        return theDescriptor.stringValue
    }
    return nil
}

if let scriptResult = runAppleScript("tell app \"Spotify\" to playpause") {
    NSLog("\(scriptResult)")
} else {
    print("no return value")
}

并带有错误处理:

enum MyAppleScriptError: ErrorProtocol {
    case ExecutingScriptFailed
    case GettingStringValueFailed
}

func runAppleScript(_ script:String) throws -> String {
    let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?>? = nil
    let startAtLoginScript = NSAppleScript(source: script)
    guard let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo) else {
        throw MyAppleScriptError.ExecutingScriptFailed
    }
    guard let value = theDescriptor.stringValue else {
        throw MyAppleScriptError.GettingStringValueFailed
    }
    return value
}

do {
    let scriptResult = try runAppleScript("tell app \"Spotify\" to playpause")
    NSLog("\(scriptResult)")
} catch {
    print(error)
}
于 2015-10-30T11:21:58.320 回答
0

我已经修复了自己的代码。

import Foundation

func runAppleScript(script:String) -> String
{
    let theResult:String
    let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()
    let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
    let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
    if let _ = theDiscriptor.stringValue
    {
        theResult = theDiscriptor.stringValue!
    } else {
        theResult = ""
    }

    return theResult
}



let scriptResult = runAppleScript("")

我必须做的是在展开之前检查 theDiscriptor.stringValue 是否有一个值。我得到的错误是因为我在打开它后试图检查值。只需删除!支票上的 即可解决我的问题。

编辑

在 Swift 3 中尝试此操作时,代码let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()不再有效。为了解决这个问题,我更新了代码。

func runAppleScript(script:String) -> String?
{
    var theResult:String?
    let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
    var errorInfo:NSDictionary? = nil
    let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(&errorInfo)
    if let _ = theDiscriptor.stringValue {theResult = theDiscriptor.stringValue!}
    return theResult
}

奖金

通过返回一个可选字符串,它允许您检查代码是否返回了一个值。

例子:

老路

let output = runAppleScript("script")
if output != ""
{
    //Script returned date
} else {
    //Script did not return data
}

新方法

if let output = runAppleScript("script")
{
    //Script returned data
} else {
    //Script did not return data
}
于 2015-10-30T11:23:49.473 回答