I have built a command line tool, at some point, I need to execute a curl command. I'm creating the script that should be executed, but I don't know how.
I'm able to create the script and printing it out, but I'm not being able to execute it.
It looks something like: curl https://api.github.com/zen
Please ask me anything if it's not clear. I appreciate your help.
#!/usr/bin/env swift
import Foundation
func run(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
run("curl", "https://api.github.com/zen")
You can run a Terminal command from Swift using NSTask (now called Process in Swift 3): If you need output, add let output = handle.readDataToEndOfFile() at the end. Here's the whole thing wrapped in a function (the launchPath would be /usr/bin/curl):
func runTask(launchPath: String, flags: [String]) -> String {
let task = Process()
let pipe = Pipe()
task.launchPath = launchPath
task.arguments = flags
task.standardOutput = pipe
let handle = pipe.fileHandleForReading
task.launch()
return String(data: handle.readDataToEndOfFile(), encoding: .utf8) ?? ""
}
In your case though, you might want to have a look at URLSession and URLRequest (superseding NSURLRequest). To create a request to your URL and credentials, you would simply do:
var request = URLRequest(url:URL(string: "https://api.github.com/zen")!)
request.setValue("application/vnd.github.v3.raw", forHTTPHeaderField: "Accept")
request.setValue("token USERTOKEN", forHTTPHeaderField: "Authorization")
let session = URLSession(configuration: .default)
session.dataTask(with: request, completionHandler: {(data, response, error) in
guard let data = data, error == nil else {
print("Error: \(error.debugDescription)")
return
}
guard let output = String(data: data, encoding: .utf8) as String? else {
print("Unable to format output data")
return
}
print(output)
}).resume()
Related
Everything is working fine, but I don't understand why the wrong label "loading" doesn't update the percentage. while i print the output value is continuously changing
do {
#discardableResult
func shell(_ args: String...) -> (String?, Int32) {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
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, task.terminationStatus)
}
shell(....)
let strURL = "http://download.com/load.zip"
let url = URL(string: strURL)
FileDownloader(url! as NSObject).download(url: url!)
var result = ""
func checkdl() {
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let fileURL = dir!.appendingPathComponent("load.txt")
result = try! String(contentsOf: fileURL, encoding: .utf8)
print(result)
if result.contains("100.0") {
try! "".write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
print("download done")
} else {
sleep(2)
loading.stringValue = result+"%"
checkdl()
}
}
checkdl()
shell(....)
}
Everything is working fine, but I don't understand why the wrong label "loading" doesn't update the percentage. while i print the output value is continuously changing
Trying to execute a call from Swift passing one parameter to PHP and getting the result
It does not execute the PHP call... not sure why?
func getInfo(_ dataValue:String){
print("in UserModel.getInfo")
let url: URL = URL(string: urlInfoPath)!
let rq = NSMutableURLRequest(url: url)
rq.httpMethod = "POST"
let postString = "a=\(dataValue)"
rq.httpBody = postString.data(using: String.Encoding.utf8)
print("PHP postString:", postString)
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) {
data, response, error in
print("UserModel.getINFO FROM PHP");
if error != nil {
print("error=\(String(describing: error))")
return
}
let val = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
self.responseString = val! as String
print("responseString = ", self.responseString)
}
}
I have a java program running in the background of my swift program. The java program can read user input in a command line. How can I pass "commands", or text line into the jar after it has been launched?
Just like the way the program down below read from the java program, how can I then "reply" to it?
let b = Bundle.main
let path = b.path(forResource: "myjar", ofType: "jar")!
NSLog("%#", "jar path : \(path)")
task.launchPath = "/usr/bin/java"
task.arguments = ["-jar", path]
let pipe = Pipe()
task.standardOutput = pipe
let errorPipe = Pipe()
task.standardError = errorPipe
task.launch()
let outHandle = pipe.fileHandleForReading
outHandle.waitForDataInBackgroundAndNotify()
var progressObserver : NSObjectProtocol!
progressObserver = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: outHandle, queue: nil){
notification -> Void in
let data = outHandle.availableData
if data.count > 0 {
if let str = String(data: data, encoding: String.Encoding.utf8) {
NSLog("%#", str)
}
outHandle.waitForDataInBackgroundAndNotify()
} else { NotificationCenter.default.removeObserver(progressObserver)
}
}
I then tried the following without luck:
pipe.fileHandleForWriting.write("text to send to java program".data(using: String.Encoding.utf8)
You're pretty close. If you just added the code pipe.fileHandleForWriting.write("text to send to java program"... that's not going to work because that pipe is the one you assigned to standardOutput.
You need to create yet another Pipe object, assign it to standardInput, and write to that:
...
>> let inputPipe = Pipe()
>> task.standardInput = inputPipe
task.launch()
>> inputPipe.fileHandleForWriting.write("text to send to java program".data(using: String.Encoding.utf8)
...
I'm a complete swift noob. Using this code in xcode I get the result I need. I created a command line binary "menubar" that takes several arguments. I normally run it in the terminal "/bin/menubar getip", "/bin/menubar/getuser". I want to create a function based on the following working code.
import Cocoa
import Foundation
var task:NSTask = NSTask()
var pipe:NSPipe = NSPipe()
task.launchPath = "/bin/menubar"
task.arguments = ["getip"]
task.standardOutput = pipe
task.launch()
var handle = pipe.fileHandleForReading
var data = handle.readDataToEndOfFile()
var result_s = NSString(data: data, encoding: NSUTF8StringEncoding)
print(result_s)
I want to convert it to a function.
func commmand (argument: String) -> String
{
let task:NSTask = NSTask()
let pipe:NSPipe = NSPipe()
task.launchPath = "/bin/menubar"
task.arguments = ["argument"]
task.standardOutput = pipe
task.launch()
let handle = pipe.fileHandleForReading
let data = handle.readDataToEndOfFile()
let result_s = NSString(data: data, encoding: NSUTF8StringEncoding)
return result_s
}
commmand getip
Try this:
func commmand(argument: String) -> String
{
let task:NSTask = NSTask()
let pipe:NSPipe = NSPipe()
task.launchPath = "/bin/menubar"
task.arguments = [argument]
task.standardOutput = pipe
task.launch()
let handle = pipe.fileHandleForReading
let data = handle.readDataToEndOfFile()
let result_s = String(data: data, encoding: NSUTF8StringEncoding)!
return result_s
}
print(commmand("getip"))
I have been following this tutorial to learn Swift and I have a few questions on the way they do things.
Specifically here:
let paramString = "data=Hello"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("Error")
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print(dataString)
}
Why is (data, response, error) in always used in NSURLSessions? What does this line of code mean? Also, why does he have a guard statement underneath?
The whole section of code is here:
func dataRequest() {
let urlToRequest: String = " http://www.kaleidosblog.com/tutorial/nsurlsession_tutorial.php"
let url: NSURL = NSURL(string: urlToRequest)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let paramString = "data=Hello"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("Error")
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print(dataString)
}
task.resume()
}
With NSURLSession you make asynchronous calls, meaning that you make / start a network request and your program continues running - it doesn't stop waiting for response. Then, when your response is ready a completion block gets called / executed. So you need a way to access the data that's coming to you with this response. This data is accessible to you with (data, response, error) properties. This are just the names of those properties, so that you know how to use them. You could have different names, but it would be confusing to anyone else.
You use the guard statement because you can't be sure that you actually have the data or the response. It could be nil if an error occurred (timeout, ...). In such case (if there's an error) you just print "Error" to the console and call return, which makes you leave the completion block without executing the lines let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding) and print(dataString). Of course, if you have the data and the response and error == nil you skip the else block of the guard statement and you just execute you last two lines of code in the block.