2015-10-29 2 views
1

Я пытаюсь создать программу в Swift 2, которая запускается и получает результат сценария AppleScript.Неожиданно найдено нуль при распаковке необязательного значения (результат 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)") 

Проблема заключается в том, что программа падает и выходы:

fatal error: unexpectedly found nil while unwrapping an Optional value

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

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

+0

Что линия это сбой на? Я мог видеть, что nslog сбой, если не было скриптаResult – bolnad

+0

@bolnad Думайте, что это была строка 'let'. Будет проверять завтра. (GMT) У меня нет доступа к моему компьютеру. – iProgram

+0

@bolnad Ошибка указана из строки 'let theResult: String = theDiscriptor.stringValue!' – iProgram

ответ

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

Если вы предпочитаете иметь значение по умолчанию, а не ноль когда это не удается, то нет необходимости возвращать Дополнительно:

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

Это хороший ответ, однако, как вы видите, я ответил сам (как раз перед вами), и у меня есть проверка функции. Это поможет сделать мой код более аккуратным позже, так как я буду запускать много AppleScripts. Я все же буду отмечать это как правильный ответ. – iProgram

+0

Спасибо. Я не заметил, что вы ответили, когда я делал мой, а потом, увидев его, я думал, что мой по-прежнему актуальен, потому что он немного безопаснее (хотя и у вас тоже все в порядке). – Moritz

+0

Ваш прием. Я также могу попытаться сделать мою функцию, чтобы выбросить ошибку. Таким образом, я могу сделать 'let scriptResult = try runAppleScript ?? «« если это возможно? – iProgram

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 
} 

Bonus

Возвращая необязательную строку, это позволяет проверить, если код возвращается значение.

Пример:

Старый способ

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 
} 
+0

@EricD Не понимал, что это небезопасно. Я просто подумал, что это лучше, потому что у него меньше строк кода. Позвольте мне проверить, работает ли это на скорости 3. Если это так, я буду повторять ваш код – iProgram

+0

@EricD Вот почему я снова посмотрел на ваш ответ из-за безопасности. – iProgram

Смежные вопросы

 Смежные вопросы