Mac Swift Cocoa - hdiutil: attach failed - Device not configured - swift

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

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

Running kextstat inside Swift app doesn't work

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

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/

Swift on MacOS: cannot open a file from my app

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)

Trying to run simple Bash command in Swift - 'Couldn't posix_spawn: error 13'

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
}