xQuartz display not for shell script launched from Swift Process - swift

I have a simple shell script which launches an X11 app. When I execute this shell script form my login shell / terminal xQuartz starts and I get a display. However the process doesn't get a display for xQuartz when running the script from within swift. Any idea how I can get the display?
Also what is the best way to detect if xQuartz is installed? Checking if xterm exists?
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/sh")
let startScriptURL = Bundle.main.url(forResource: "run", withExtension: "sh")
guard let startScriptPath = startScriptURL?.path else {
return
}
process.arguments = [startScriptPath]
do {
try process.run()
} catch let error {
print(error)
}
run.sh:
#!/bin/sh
/opt/X11/bin/xeyes

I figured our how to pass the DISPLAY environment or any environmental variable to Process.
The current environment can be obtained by:
ProcessInfo().environment
So I use now this:
let process = Process()
guard let envDisplay = ProcessInfo().environment["DISPLAY"] else {
print("Please install xQuartz")
return
}
process.environment = ["DISPLAY": envDisplay]
I got the idea from here: Issue launching X11 app via NSTask

Related

Enable touchid in the Apple Script in Swift

NSAppleScript(source: "do shell script \"sudo chmod 777 '\(appPath.path)'\" with administrator " + "privileges")!.executeAndReturnError(nil)
When I run this code in Xcode, the app gives a alert
But if I run same code in the Apple Scrip app, the app will give a touch id alert.
How can I show a touch id alert when I run this code in the Xcode?
Finally, I found it is impossible to do this. But there is an alternative way.s
guard let shBundlePath = Bundle.main.path(forResource: "chmodHelper", ofType: "sh") else { fatalError("Cannot get the sh file path") }
guard let installerHelperPath = Bundle.main.path(forResource: "installHelper", ofType: "sh") else { fatalError("Cannot get the sh file path") }
let helperToolURL = URL.documents.universalappending(path: "chmodHelper.sh")
if FileManager.default.fileExists(atPath: helperToolURL.universalPath()) {
try FileManager.default.removeItem(at: helperToolURL)
}
try FileManager.default.copyItem(atPath: shBundlePath, toPath: helperToolURL.universalPath())
try setInstallerContent(path: helperToolURL.universalPath())
NSAppleScript(source: "do shell script \"chmod +x '\(installerHelperPath)' && sudo '\(installerHelperPath)'\" with administrator " + "privileges")!.executeAndReturnError(nil)
Run this when the app starts.
And then change the content of the shell file. Then use apple scrip to run it. So you can avoid the alert

How to run Terminal commands from cocoa app?

I have problems running a Terminal command from a Cocoa Application.
The input for the Terminal is real easy: /Users/.../Csvmidi </Users/.../test.csv> /Users/.../Melody.mid
These are three inputs- three actual paths - are just written in a row and seperated by a spac: the first "Csvmidi" runs a Unix Application which converts the test.csv to an actual hearable MIDI file. Through the terminal it works perfectly...
I just don't get it to work via a Cocoa Application.
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/zsh/")
process.arguments = [lblCsvmidi.stringValue,"<"+lblURL.stringValue+">",lblMidi.stringValue]
//I saved the URL of the UNIX program, test.csv and Melody.mid in a lable just to be sure.
//lblCsvmidi --> URL of Csvmidi UNIX program
//lblURL --> URL of test.csv
//lblMidi --> URL of Melody.mid
print(lblCsvmidi.stringValue,"<" + lblURL.stringValue + ">",lblMidi.stringValue)
// this print command was only made to check the arguments in the terminal if they would work --> they do
process.terminationHandler = { (process) in
print("\ndidFinish: \(!process.isRunning)")
}
do {
try process.run()
} catch {}
When I run the code it gives me either the error of an 75: unmatched", but actually there isn't a quotation mark in the command - or I get "permission denied" errors.
I tried several bin folders like ( I really tried almost every possible):
- /bin/zsh
- /bin/csh
- /bin/ksh
- ...
What am i doing wrong --> I haven't found information in other Questions here and the Process, NSTask and Bundle informations from Apple haven't helped me so far.
Thanks!!
The following runs without error on my system:
import Cocoa
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/zsh/")
var args : [String]!
args = []
args.append("-c")
args.append("open '/Users/xxxx/Desktop/myApp.app/Contents/MacOS/myApp'")
process.arguments = args
process.terminationHandler = { (process) in
print("\ndidFinish: \(!process.isRunning)")
}
do {
try process.run()
} catch {}
let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.activate(ignoringOtherApps:true)
app.run()
The strings after "-c" would depend on what you are trying to accomplish.

Process.run Returns 'The file “<command>” doesn’t exist.'

I'm trying to write a small app to start/stop and display data from a command line "app" someone else wrote. The command executable is installed in '/usr/local/bin'. It outputs status text data to standardOutput while running. I can execute this "command" from the Terminal.app without issue. From swiftUI code I can successfully execute "built-in" commands like ls. However, when (in swiftUI code) I attempt to execute Process.run WITH the new command it throws the exception 'The file “” doesn’t exist.'
Anyone have any ideas? Thanks in advance!
Here's a code snip:
// NOTE: "installedCommand" is just a placeholder for the actual command.
let task = Process()
let connection = Pipe()
let exeUrl = URL(fileURLWithPath: "/usr/local/bin/installedCommand")
//let exeUrl = URL(fileURLWithPath: "/bin/ls") <--works fine
task.executableURL = exeUrl
task.standardOutput = connection
do
{
try task.run()
}
catch
{
print("Error: \(error.localizedDescription)")
return
}

How to have access to files via shell commands in mac os x application?

Im new to developing mac applications. (but I have work a lot for ios)
For start I want to have a window on which there is a button. When user click on the button, it should trigger an action that will run some shell commands inside the folder that the user dragged & dropped inside the app before.
For example
User will drag and drop some folder inside my app and the app should create gemfile inside this folder.
Here is my current version where I run the shell and the first command is cd <pathToFolder> but the output is /usr/bin/cd: line 4: cd: file:///Users/klemen/Downloads/ABCAcura/: No such file or directory
Here is my code:
#IBAction func handleCreateGemfileButton(_ sender: NSButton) {
let pipe = Pipe()
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = ["cd", projectURL.absoluteString]
process.standardOutput = pipe
let file = pipe.fileHandleForReading
process.launch()
if let result = NSString(data: file.readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) {
print(result as String)
}
else {
print("error")
}
}
Is there some restrictions. How is it possible to request from user that my app will have access to some of his files?
You should use ~/Downloads/ABCAcura instead of file:///..., file:/// is a protocol (URL) not a path to file.

Getting success result on shell script executing installer pkg within an NSTask Swift

I am building an installer for an OS X application in Swift 2.0. The installer does little more than take user input, sends it to a server, and builds a desktop shortcut determined by server response. This shortcut (the 'application') opens up another application (FileMaker). As part of the installation process I am installing FileMaker silently using NSTask which runs a shell script executing installer.app on the FileMaker .pkg. This works fine.
I need to determine whether this installation is successful or not in order to progress to the next step in the installer. I can easily get the string response from the terminal, ie "installer: The install was successful," but I dont feel a hard coded string condition is robust enough. Any other possibilities?
n.b. I'm a beginner Swift developer (1 week!) and have only a year of Web Development behind that. ie. I'm blindingly green.
P.S. Ideally I'd be displaying a progress indicator for the FileMaker installation rather than a message, but that'd be overextending myself (further) if it's even possible.
func installFileMaker() {
let fileMakerFileName = "FileMaker Pro 14"
let fileMakerDirectory = "Resources/FileMaker14_MacClient"
// get resource path
let bundle = NSBundle.mainBundle()
let resourcePathResult = bundle.pathForResource(fileMakerFileName, ofType: "pkg", inDirectory: fileMakerDirectory)
if let resourcePath = resourcePathResult {
displayWebViewMessage("Installing Briefcase Now...")
let command = "installer -pkg \"" + resourcePath + "\" -target /"
Utilities.runAsCommandInBackground(command, callback: installUpdateWebviewCallback)
} else {
// error
displayWebViewMessage("Installation error")
}
print("rrr")
}
Runs shell command
static func runAsCommandInBackground(command: String, callback: (((success:Bool, message:String)) -> Void)?) {
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
let pipe = NSPipe()
let task = NSTask()
task.launchPath = "/bin/sh"
task.arguments = ["-c", String(format:"%#", command)]
task.standardOutput = pipe
let file = pipe.fileHandleForReading
task.launch()
var result = ""
if let tmpResult = NSString(data: file.readDataToEndOfFile(), encoding: NSUTF8StringEncoding) {
result = tmpResult as String
} else {
// error
}
dispatch_async(dispatch_get_main_queue()) {
print(result)
// Give me a more robust result!!
if let unwrappedCallback = callback {
unwrappedCallback((true, result as String))
}
}
}
}