首页 文章

打开可选值时意外发现nil(AppleScript结果)

提问于
浏览
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)")

问题是程序崩溃和输出:

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

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

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

2 回答

  • 1

    实际上错误可能来自 NSAppleScript(source: script)! 所以正确的解决方案是返回一个Optional String而不是使用force unwrapping:

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

    Swift 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)
    }
    
  • 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是否有值 . 我得到的错误是因为我在打开包装后试图检查该值 . 只需删除支票上的 ! 即可修复我的问题 .

    Edit

    在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
    }
    

    Bonus

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

    例:

    Old way

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

    New way

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

相关问题