Неожиданно обнаружен ноль при развертывании необязательного значения (результат AppleScript)

Я пытаюсь сделать программу в 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)")

Проблема в программе вылетает и выводит:

фатальная ошибка: неожиданно найден ноль при развертывании необязательного значения

в консоли. Я также пытался if let elseОднако это тоже не работает. Как бы я исправить эту проблему?

Это было проверено с использованием шаблона командной строки OS X с использованием языка swift.

2 ответа

Решение

На самом деле ошибка может исходить от 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)
}

Я исправил свой собственный код.

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
}
Другие вопросы по тегам