How to get top output with swift 3 on osx (NOT iOS) - macos

// 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()

Related

Run shell .sh script in Terminal from MacOs SwiftUI App

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)
}

Download a file to a temp location in Swift 4 (Mac OS) like with curl

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()
}

Setting the Desktop background on OSX using Swift 2

After an introduction to Javascript, I'm trying to tackle OSX/iOS programming by creating some simple tools to scratch my own itches.
However, right from the jump I hit a roadblock.
I found two examples that should work.
https://github.com/hinderberg/ios-swift-kurs/blob/master/swift-intro/wallpaper.swift
https://www.snip2code.com/Snippet/196825/Swift-shell-script-to-randomize-wallpape
Here's the second:
#!/usr/bin/env xcrun swift
import Foundation
import AppKit
let imagesDir = "/Users/david/Dropbox/Graphics/Wallpaper-HD/"
var err: NSError?
let fs = NSFileManager.defaultManager()
let filenames = fs.contentsOfDirectoryAtPath(imagesDir, error: &err) as [String]?
if let error = err {
NSLog(error.localizedDescription)
} else {
let imagenames = filenames!.filter { $0.hasSuffix(".jpg") || $0.hasSuffix("png") }
let ir = Int(arc4random_uniform(UInt32(imagenames.count)))
let imgurl = NSURL.fileURLWithPath(imagesDir + imagenames[ir])
let workspace = NSWorkspace.sharedWorkspace()
let screen = NSScreen.mainScreen()
let ok : Bool = workspace.setDesktopImageURL( imgurl!, forScreen: screen!, options: nil, error: nil )
if ok {
println( "New wallpaper: " + imagenames[ir] )
} else {
println("Oops!")
}
}
This didn't work in XCode 7 beta 3.
Hoping to reduce to the essentials, I arrived at:
#!/usr/bin/env xcrun swift
import Foundation
import AppKit
let imagesDir = "/Users/josh/Downloads/"
let singleImage = "/Users/josh/Downloads/xlarge.png"
let imgurl = NSURL.fileURLWithPath(singleImage)
let workspace = NSWorkspace.sharedWorkspace()
let screen = NSScreen.mainScreen()
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen!, options: nil, error: nil )
if ok {
print( "New wallpaper set!" )
} else {
print("Oops!")
}
And saved as the file wallpaper.swift.
On execution, the error is:
./wallpaper.swift:17:49: error: extra argument 'error' in call
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen!, options: nil, error: nil )
And now I'm completely stuck...
I've tried referring to NSWorkspace and NSScreen documentation as well as running through playground, but it's beyond my current skills.
Removing the extra argument it complains about (error: nil) simply gives a different error:
./wallpaper.swift:13:31: error: cannot invoke 'setDesktopImageURL' with an argument list of type '(NSURL, forScreen: NSScreen?, options: nil)'
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen, options: nil )
Where is the code failing, and how can I understand how to make it work properly?
In your example you're passing nil as options to the method.
I guess it worked before but now in the comments you showed the current method signature:
(url: NSURL, forScreen screen: NSScreen, options: [String : AnyObject]) throws
We see that options should be a non-Optional Dictionary.
It means you can't use nil anymore for the options parameter: if you don't have options, just pass an empty Dictionary.
Also, now in Swift 2, this method doesn't return a Bool anymore, it throws.
Meaning you have to use it with do try catch:
do {
let imgurl = NSURL.fileURLWithPath(singleImage)
let workspace = NSWorkspace.sharedWorkspace()
if let screen = NSScreen.mainScreen() {
try workspace.setDesktopImageURL(imgurl, forScreen: screen, options: [:])
}
} catch {
print(error)
}
Updated example for Swift 3:
do {
let imgurl = NSURL.fileURL(withPath: singleImage)
let workspace = NSWorkspace.shared()
if let screen = NSScreen.main() {
try workspace.setDesktopImageURL(imgurl, for: screen, options: [:])
}
} catch {
print(error)
}
For those of us using Xamarin Mac, this can be achieved like so:
string filepath = "/Users/you/Desktop/sweet-wallpaper.jpg";
var workspace = NSWorkspace.SharedWorkspace;
var screen = NSScreen.MainScreen;
NSUrl url = NSUrl.FromFilename(filepath);
NSDictionary options = new NSDictionary();
NSError errorContainer = new NSError();
workspace.SetDesktopImageUrl(url, NSScreen.MainScreen, options, errorContainer);
Updated version for Swift 5.x:
do {
let imageURL = URL(fileURLWithPath: "/path/to/image")
if let screen = NSScreen.main {
try NSWorkspace.shared.setDesktopImageURL(imageURL, for: screen, options: [:])
}
} catch {
print(error)
}

Swift error: launch path not accessible

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"

NSTask launchPath not working

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].

Resources