iProgram iProgram - 4 months ago 39
Swift Question

Unexpectedly found nil while unwrapping an Optional value (AppleScript result)

I am trying to make a program in Swift 2 that runs and gets the result of an AppleScript script.

Here is my code:

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


The problem is the program crashes and outputs:


fatal error: unexpectedly found nil while unwrapping an Optional value


in the console. I have also tried
if let else
, however that does not work either. How would I fix this issue?

This was tested using a OS X Command Line template using the swift language.

Answer

I have fixed my own code.

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

What I had to do was check if theDiscriptor.stringValue has a value before unwrapping it. The error that I was getting is because I was trying to check the value after I had unwrapped it. Simply removing the ! on the check fixed my problem.

Edit

When trying this in Swift 3, the code let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>() no longer works. To fix this, I have updated the code.

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

By returning an optional string, it allows you to check if the code returned a value.

Example:

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