2

I wrote a simple OSx (10.11) application to execute shell commands when a button is pressed. It works when I run it from xcode, but when I export the application via "archive", one of the buttons no longer works. I don't get an error and I don't get any output either. I am using absolute paths so I don't understand why it works in xcode but not as an exported application, Nor do I understand why one button works and the other doesn't.

Here is the main function that I am using the make the shell commands

    func runCommand(path : String, args : [String]) -> (output: [String], error: [String], exitCode: Int32) {
    var output : [String] = []
    var error : [String] = []

    let task = NSTask()
    task.launchPath = path
    task.arguments = args

    let outpipe = NSPipe()
    task.standardOutput = outpipe
    let errpipe = NSPipe()
    task.standardError = errpipe

    task.launch()

    let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
    if var string = String.fromCString(UnsafePointer(outdata.bytes)) {
        string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
        output = string.componentsSeparatedByString("\n")
    }

    let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
    if var string = String.fromCString(UnsafePointer(errdata.bytes)) {
        string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
        error = string.componentsSeparatedByString("\n")
    }

    //task.waitUntilExit()
    let status = task.terminationStatus

    return (output, error, status)
}

and here is the button that works:

      // Check for configurator 2 app installation
    let (output, error, status) = self.runCommand("/bin/bash", args:  ["-c", "/bin/ls", "/Applications/Apple Configurator 2.app"])

and here is the button that doesn't:

        // Check if the phone is plugged in and paired
    let (output, error, status) = self.runCommand("/bin/bash", args: ["-c", "/usr/local/bin/cfgutil", "get", "isPaired"])

What is even more strange, I discovered (through sheer frustration) that if I repeatedly click the button that doesn't work, it will sometimes eventually work.

AllenMoh
  • 476
  • 2
  • 13
  • For a moment I thought maybe I could help you debug this, but you lost me at "I discovered [...] it will *sometimes* eventually work"... Are you sure about your observations? – Eric Aya Mar 10 '16 at 20:51
  • I am sure about this. I put in an alert message that would show me the output of the commands when the click didn't work and there is no output (""), no error output either – AllenMoh Mar 11 '16 at 23:21
  • 1
    Are all the `if var string` successfull? Did you try adding `else` branches? Since you have default values, it could be the reason for the silent failures. – Eric Aya Mar 11 '16 at 23:37
  • Thanks, that was the problem. You should post the answer so you get credit – AllenMoh Mar 15 '16 at 18:27

2 Answers2

1

Your issue is the result of two things happening together:

  • you return default values

  • you don't specify alternative branches for the control flow

What happens is that it hides potential failures, and leads to code that is very hard to debug, as you experienced.

A possible solution with your existing code is to cover all possible ways, meaning providing else branches to your if var string = String.fromCString(UnsafePointer(errdata.bytes)) conditions, where you will handle errors.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
0

Thanks to Eric D. I simplified my code and now everything is working.

func runCommand(path : String, args : [String]) -> (output: NSString, error: NSString, exitCode: Int32) {
    let task = NSTask()
    task.launchPath = path
    task.arguments = args

    let outpipe = NSPipe()
    task.standardOutput = outpipe
    let errpipe = NSPipe()
    task.standardError = errpipe

    task.launch()

    let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
    let output = NSString(data: outdata, encoding:  NSUTF8StringEncoding)

    let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
    let error_output = NSString(data: errdata, encoding: NSUTF8StringEncoding)

    task.waitUntilExit()
    let status = task.terminationStatus

    return (output!, error_output!, status)
}
AllenMoh
  • 476
  • 2
  • 13