I have an app that needs to restart the dock application. I have tried this with both Apple Script:
var errorDict: NSDictionary? = nil
let appleScript = NSAppleScript(source: "tell application \"Dock\" to quit")
var error = appleScript?.executeAndReturnError(&errorDict)
if let errorDict = errorDict {
println("An error occured: \(errorDict)")
}
... and NSTask:
let task = NSTask()
task.launchPath = "/usr/bin/killall"
task.arguments = ["Dock"]
task.launch()
... and another NSTask:
func restartFinder () {
let task = NSTask()
task.launchPath = "/bin/bash"
task.arguments = ["killall Dock"]
task.launch()
}
However, it seems my app is not allowed to restart it. I'd like to release my app to the AppStore, but how can I restart the dock?
Error when using Apple Script:
An error occured: {
NSAppleScriptErrorAppName = Dock;
NSAppleScriptErrorBriefMessage = "Application isn\U2019t running.";
NSAppleScriptErrorMessage = "Dock got an error: Application isn\U2019t running.";
NSAppleScriptErrorNumber = "-600";
NSAppleScriptErrorRange = "NSRange: {27, 4}";
}
Error when using NSTask:
killall: warning: kill -TERM 255: Operation not permitted
Update
I have also tried it with STPrivilegedTask, which didn't work for me either. Neither did I get an auth window.
I would try using Applescript, but instead like this:
var errorDict: NSDictionary? = nil
let appleScript = NSAppleScript(source: "do shell script \"killall Dock\" with administrator " +
"privileges")
var error = appleScript?.executeAndReturnError(&errorDict)
if let errorDict = errorDict {
println("An error occured: \(errorDict)")
}
This way, it executes the shell script(as if through Terminal) with admin privileges. The problem is since this is a task normally only done by the system or the user, it requires you to type in the admin password.
Related
I want to create a SwiftUI MacOs App to run shell scripts from my App.
Therefore I want to open the Terminal and paste a .sh or the content of this script and run it.
First I got an permission denied error which I solved by removing the sandbox mode.
But now all commands within the script like npm or mvn lead me to the error:
"zsh:1: command not found: npm\n"
Do you have a solution for my problem?
struct ContentView: View {
var title : String
var body: some View {
VStack {
Button(action: {
runCommand()
}) {
Text("Run")
}
}
}
}
func runCommand() {
let path = "/Users/User1/Desktop/test.sh"
print( shell("/Users/User1/Desktop/test.sh start") )
}
func shell(_ command: String) -> (String?) {
let task = Process()
task.launchPath = "/bin/zsh"
task.arguments = ["-c", command]
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'm writing a CLI tool in Swift 4 for MacOS.
If I "cheat" and use a shell function, then the curl command, I get exactly the result I'm after:
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
shell("/usr/bin/curl", "-OL", "http://mywebserver/myfile.whatever")
How do I do the same thing natively in Swift 4?
I tried using the just library: https://github.com/dduan/Just
but I can't figure out how to simply get it to download a file from a URL to a specific location on the file system.
I've also found various solutions for Swift 3 and 5, and iOS which obviously don't work for me.
Simple example: Due to the asynchronous behavior of URLSession you have to start the runloop after resuming the data task and stop it in the completion handler.
let runLoop = CFRunLoopGetCurrent()
let url = URL(string: "http://mywebserver/myfile.whatever")!
let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
let returnValue : Int32
if let error = error {
fputs("\(error.localizedDescription)\n", stderr)
returnValue = EXIT_FAILURE
} else {
let result = String(data:data!, encoding:.utf8)!
fputs("\(result)\n", stdout)
returnValue = EXIT_SUCCESS
}
CFRunLoopStop(runLoop)
exit(returnValue)
}
dataTask.resume()
CFRunLoopRun()
The code returns the data in stdout. You can write the data directly to disk, replace
let result = String(data:data!, encoding:.utf8)!
fputs("\(result)\n", stdout)
with
let fileURL = URL(fileURLWithPath: "/Users/me/path/to/file.ext")
do {
try data!.write(to: fileURL)
} catch {
fputs("\(error.localizedDescription)\n", stderr)
}
You can download using downloadTask
Create session configuration
Create URLSession with this configuration
Apply download task
When the download is done successfully, copy the data to the destination
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.jpg")
//Create URL to the source file you want to download
let fileURL = URL(string: "https://en.wikipedia.org/wiki/Saint_Martin#/media/File:Saint_martin_map.PNG")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
task.resume()
}
// Playground
import Foundation
let task = Process()
task.launchPath = "/usr/bin/top"
task.arguments = ["-s","2"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
task.waitUntilExit()
print(String(data: data, encoding: String.Encoding.utf8)!)
This code work with "/bin/ls" instead of "/usr/bin/top" and when i put others argument but like it is actualy i get nothing on playground and it crach in my xcode8 project with an "An uncaught exception was raised" and it lunch a debuger with asm.So how to get TOP output in a variable ?
Found
// Playground
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let task = Process()
task.launchPath = "/usr/bin/top"
task.arguments = ["-s","9"]
let pipe = Pipe()
task.standardOutput = pipe
let outHandle = pipe.fileHandleForReading
outHandle.readabilityHandler = { pipe in
if let line = String(data: pipe.availableData, encoding: String.Encoding.utf8) {
// Update your view with the new text here
print("New ouput: \(line)")
} else {
print("Error decoding data: \(pipe.availableData)")
}
}
task.launch()
I'm doing a simple OSX App to show/hide hidden files in the Finder from a StatuBar menu.
This is the IBAction to show/hide files:
#IBAction func menuClicked(sender: NSMenuItem) {
let task = NSTask()
task.currentDirectoryPath = "/var/tmp/"
task.launchPath = "usr/bin/defaults"
if(sender.state == NSOnState){
sender.state = NSOffState
task.arguments = ["write", "com.apple.finder", "AppleShowAllFiles", "NO"]
}else{
sender.state = NSOnState
task.arguments = ["write", "com.apple.finder", "AppleShowAllFiles", "YES"]
}
task.launch()
task.waitUntilExit()
let killTask = NSTask()
killTask.launchPath = "usr/bin/killall"
killTask.arguments = ["Finder"]
killTask.launch()
}
This gives me this error:
2015-05-10 23:54:22.237 ShowHideFiles[1234:303] An uncaught exception was raised
2015-05-10 23:54:22.238 ShowHideFiles[1234:303] launch path not accessible
I tried to find out why but cannot find an answer.
I tried also to see which of the two launchPath was wrong by disabling one or the other and they both give the same error.
Can somebody help me?
Both var and usr are at the same level, so you need to prefix usr with / like you did for var:
task.currentDirectoryPath = "/var/tmp/"
task.launchPath = "/usr/bin/defaults"
killTask.launchPath = "/usr/bin/killall"
I have a NSOpenPanel where the user choses a directory and I'm trying to run an NSTask but the launchPath isn't working.
Here's my code:
#IBAction func choseFile(sender: AnyObject) {
var panel = NSOpenPanel()
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = false
panel.beginWithCompletionHandler { (result) -> Void in
if result == NSFileHandlingPanelOKButton {
var path : String = panel.URL!.absoluteString as String!
var filePath = path.stringByReplacingOccurrencesOfString("file://", withString: "", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
let task = NSTask()
task.launchPath = filePath
task.arguments = ["ls"]
task.launch()
task.waitUntilExit()
}
}
}
Thanks :)
The launchPath of an NSTask is the path to the program to run, not the working directory.
I assume, since you set the arguments to be ls, that you want to run the ls command in a particular directory. In that case, you want to set the launchPath to "/bin/ls" and the arguments to [panel.URL!.path].