I am creating a MacOS app that automatically installs .dmg's in user Applications folder (copies .app in a dmg to /Applications). But I need to somehow attach the selected .dmg image. I have tried doing this:
func dragViewDidReceive(fileURLs: [URL]) {
for url in fileURLs {
print(url.path)
print(shell("hdiutil attach \(url.path)"))
}
}
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
But if I select the file (aka call dragViewDidReceive function) the dmg will not attach and the output for bash command will be hdiutil: attach failed - Device not configured
I also tried using ShellOut:
func dragViewDidReceive(fileURLs: [URL]) {
for url in fileURLs {
print(url.path)
try! shellOut(to: "hdiutil attach \(url.path)")
}
}
Which gives the same error. hdiutil: attach failed - Device not configured
Is there any other way how I can attach a .dmg? Or is there any fix for this error using one of these methods?
Thanks
Related
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
I'm trying to run the following shell script that check if a kext is loaded in a Swift app.
kextstat -l
When I run it from xcrun swift, it works fine. However it doesn't work inside an app.
It returns an internal error:
Error Domain=KMErrorDomain Code=71 "Kernel request failed: (libkern/kext) internal error (-603947007)" UserInfo={NSLocalizedDescription=Kernel request failed: (libkern/kext) internal error (-603947007)}
My app is not sandboxed. How can I do this?
CODE
import Foundation
func getKextStatus(_ kextID: String) -> Bool {
do {
return try shell("/usr/sbin/kextstat", ["-l"]).contains(kextID)
} catch {
return false
}
}
func shell(_ file: String, _ args: [String]) throws -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = args
task.executableURL = URL(fileURLWithPath: file)
do {
try task.run()
}
catch { throw error }
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
// example
getKextStatus("com.apple.Dont_Steal_Mac_OS_X")
I'm running macOS Monterey 12.1, Xcode 13
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/
I'm writing an app that displays a list of PDF files in a NSTableView that the user should be able to double-click to open in the default application (Preview, Adobe Reader, ...).
I've tried using NSWorkspace.shared.openFile and NSWorkspace.shared.open(_: withAppBundleIdentifier: options: additionalEventParamDescriptor: launchIdentifiers:), but none of them work.
I can't use the new func open(URL, configuration: NSWorkspace.OpenConfiguration, completionHandler: ((NSRunningApplication?, Error?) -> Void)?) as this is targeted at pre-Catalina computers.
Here are the two code snippets:
1.
#objc func doubleClickOnResultRow() {
let clickedRow = resultsTableView.selectedRow
if ( clickedRow > -1 ) {
let myURL = foundItems[clickedRow] as URL
if (!NSWorkspace.shared.openFile(myURL.path)) {
print("Unable to open : ", myURL.path)
}
}
}
This first one does nothing, and I get a "Unable to open : url/to/my/file.pdf" in the console.
2.
#objc func doubleClickOnResultRow() {
let clickedRow = resultsTableView.selectedRow
if ( clickedRow > -1 ) {
let myURL = foundItems[clickedRow] as URL
NSWorkspace.shared.open([myURL], withAppBundleIdentifier: "com.apple.Preview", options: NSWorkspace.LaunchOptions.withErrorPresentation, additionalEventParamDescriptor: nil, launchIdentifiers: nil)
}
}
With this one, however, when I double-click on a file, I get a error window with the Finder icon that says :
"The application “My app” does not have permission to open “myfile.pdf.” Here is the screenshot:
Finder error
I don't understand what I'm doing wrong. Eventually I could build a lightweight PDF viewer inside my app, but I would really like to avoid it if possible.
EDIT 03/31/2020, 16:10
I've tried a third way, by calling the shell command "open" with this (found here):
func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
and then
let command = "open \""+myURL.path+"\""
print(shell(command))
and I get
LSOpenURLsWithRole() failed with error -54 for the file path/to/my/file.pdf.
You have to construct the URL to file using init(fileURLWithPath:) of URL not the usual init?(string:)
let url = URL(fileURLWithPath: "/path/to/file.pdf")
Then you can open it with default application using NSWorkspace.open instance method
NSWorkspace.shared.open(url)
I'm trying to build a MacOS app that can run bash commands from GUI input and have fallen at the first hurdle. I've been using this question's answer as a reference but it doesn't seem to work for me. This is my code:
import Foundation
#IBAction func buttonClicked(_ sender: Any) {
shell("ls")
}
#discardableResult
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/Users/myUser/desktop"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
I've seen others asking about this error but they are getting it for slightly different reasons meaning I can't seem to find a fix for my particular instance of the problem.
Any ideas?
EDIT - Would also be great if someone could give me a hint as to how to get the output of the ls command back into my program, to store as a string for example.
For a simple terminal app this is what I used
func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output = String(data: data, encoding: .utf8) else {
print("Failed to produce string from \(data)")
abort()
}
return output
}