Running command from Cocoa freezes my app - swift

I use this code to run a command from the Cocoa Application:
extension String {
func runAsCommand() -> String {
let pipe = NSPipe()
let task = NSTask()
task.launchPath = "/bin/sh"
task.arguments = ["-c", String(format:"%#", self)]
task.standardOutput = pipe
let file = pipe.fileHandleForReading
task.launch()
if let result = NSString(data: file.readDataToEndOfFile(), encoding: NSUTF8StringEncoding) {
return result as String
}
else {return "--- Unable to initialize string from file data ---"}
}
}
Example:
"command".runAsCommand()
The problem is, that this freezes my application. For example, if the command is to open some other app, such as Firefox, it freezes my host app until Firefox is exited. I don't want that behaviour. What should I do?

Related

How to launch terminal and pass it a command using Swift

I want to programmatically open a terminal and paste a command into it like "cd /Users/...".
I can start a terminal using this code, but I don't know how to execute command
guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Terminal") else { return }
let path = "/bin"
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = \[path\]
NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil)
It is very important to use the sandbox, the Process command is not suitable.
If all you are trying to achieve is to open a terminal on a specific location all you have to do is to use this code:
let pathToOpen = "/Users/admin/Desktop"
let url = URL(string:"terminal://"+pathToOpen)!
NSWorkspace.shared.open(url)
If you are trying just to run a command in terminal and show the output in your app, here is another useful code snippet:
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.standardInput = nil
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
//Usage:
shell("yourCommandHere")
//please note that you are not allowed to use commands that require sudo

macOS Swift Launch Location Not Accessible

I want to execute the following shell command from my macOS app:
/Users/macuser/Desktop/videos/ffmpeg -i /Users/macuser/Desktop/videos/vid1.mov -vf fps=1/1 /Users/macuser/Desktop/videos/images/out%0d4.jpg
The method I'm using to do this is:
func shell(_ launchPath: String, _ arguments: [String]) -> String?
{
let task = Process()
task.launchPath = launchPath
task.arguments = arguments
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
task.waitUntilExit()
return output
}
I am passing the arguments to the method like this:
let command = ffmpegLocation
let arguments = [
"-i",
videoLocation,
"-vf",
"fps=1/1",
"\(outputLocation)/out%0d4.jpg"
]
resultsField.stringValue = shell(command,arguments)!
What I get is "launch path is not accessible". I have verified the command does work in Terminal. Do I need to use a different approach?

How can I create Daemon in LaunchAgents from macOS APP?

I want to create Daemon (Plist in ~/Library/LaunchAgents/) from my macOS application.
import Foundation
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/bash"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
// Example usage:
shell("ls -la")
I know using this I can create a daemon but at this location
~/Library/LaunchAgents/
I am not able to create due to permission issues. Operation not permitted
But using terminal I am able to Create Plist inside
~/Library/LaunchAgents/

How to show real time result while using terminal command in a swift script

I am using Xcode Project Renamer to rename my Xcode project, after that I used the code below to install pod file.
It's working good but the terminal showing result after pod installed. I want to show result while installing the pod.
#discardableResult
private func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
// pipe.fileHandleForReading.readDataToEndOfFile()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
let command = shell("pod install")
print(command)
Can someone help please.

How to use Process() in Swift 3 for Linux?

The following function executes a process in Swift 3 on macOS. But if I run the same code in Ubuntu I get the error that Process is an unresolved identifier.
How do I run a process / task in Swift 3 for Ubuntu and get its output?
import Foundation
// runs a Shell command with arguments and returns the output or ""
class func shell(_ command: String, args: [String] = []) -> String {
let task = Process()
task.launchPath = command
task.arguments = args
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String? = String(data: data,
encoding: String.Encoding.utf8)
task.waitUntilExit()
if let output = output {
if !output.isEmpty {
// remove whitespaces and newline from start and end
return output.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
return ""
}
I cannot test it myself currently, but according to the source code
https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSTask.swift,
the corresponding class is (still) called Task on Linux, not Process
as on Apple platforms.