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.
Thanks to Eric D. I simplified my code and now everything is working.
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 yourif var string = String.fromCString(UnsafePointer(errdata.bytes))
conditions, where you will handle errors.