macos - programar - Usando NSURLSession desde un programa de línea de comandos Swift
swift tutorial español (4)
Estoy tratando de probar una pequeña aplicación de línea de comandos de prueba de concepto antes de integrarla en una aplicación más grande. Lo que estoy tratando de hacer es descargar algunos datos usando NSURLSession usando este ejemplo . Sin embargo, parece que si utilizo los ejemplos dados en una aplicación de línea de comandos simple de OS X, la aplicación se cierra antes de que se recuperen los datos.
¿Cómo puedo descargar datos desde una aplicación de línea de comandos independiente utilizando NSURLSession? Lo que he leído es sobre el uso de NSRunLoop sin embargo, todavía no he encontrado un ejemplo claro en Swift, por lo que si NSRunLoop es el camino a seguir, se agradecería cualquier ejemplo.
Cualquier otra estrategia para descargar datos desde una URL para una aplicación de línea de comandos Swift también es bienvenida (¿bucle infinito de tiempo?).
Prueba esto
let sema = DispatchSemaphore( value: 0)
let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg")!;
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
print("after image is downloaded");
sema.signal(); // signals the process to continue
};
task.resume();
sema.wait(); // sets the process to wait
Puede usar un semáforo para bloquear el hilo actual y esperar a que finalice su sesión de URL.
Cree el semáforo, inicie su sesión de URL, luego espere en el semáforo. Desde la devolución de llamada de finalización de su sesión de URL, señale el semáforo.
Podría usar un indicador global (declarar una variable booleana volátil) y sondearlo desde un bucle while, pero eso es menos óptimo. Por un lado, estás quemando ciclos de CPU innecesariamente.
Aquí hay un ejemplo rápido que hice usando un patio de recreo:
import Foundation
var sema = DispatchSemaphore( value: 0 )
class Delegate : NSObject, URLSessionDataDelegate
{
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
{
print("got data /(String(data: data, encoding: .utf8 ) ?? "<empty>")");
sema.signal()
}
}
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: Delegate(), delegateQueue: nil )
guard let url = URL( string:"http://apple.com" ) else { fatalError("Could not create URL object") }
session.dataTask( with: url ).resume()
sema.wait()
Resolví esto con un bucle while y un indicador bool que se establece en verdadero cuando se devuelve la sesión url
Xcode> MacOS> aplicación de consola
cambio principal
import Foundation
//calls api to send push message but call is async and console app may have died before ws call returns
func sendRequest() {
print("sendRequest called")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
guard var URL = URL(string: "https://api.pushover.net/1/messages.json") else {return}
let URLParams = [
"user": "USER",
"token": "TOKEN",
"message": "New+PUSH",
]
//helper method to append params
URL = URL.appendingQueryParameters(URLParams)
var request = URLRequest(url: URL)
request.httpMethod = "POST"
/* Start a new Task */
print("sendRequest called: POST ")
//FLAG to use to keep app around till WS returns
var keepRunning = true
let task = session.dataTask(with: request,
completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
if (error == nil) {
// Success
print("sendRequest called: POST RETURNED Success")
let statusCode = (response as! HTTPURLResponse).statusCode
print("URL Session Task Succeeded: HTTP /(statusCode)")
}
else {
// Failure
print("URL Session Task Failed: %@", error!.localizedDescription);
}
//WS - returns async and reponse handled - flip bool to kill while loop below and also kills app
print("WS reponse handled set keepRunning to false")
keepRunning = false
})
task.resume()
//session.finishTasksAndInvalidate()
//Issue: console app will die when doSearch() ends, but ws call may not have returned so session may die and ws call may fail so add while loop to prevent that
while keepRunning {
print("keepRunning: true: loop")
}
print("keepRunning:/(keepRunning) - sendRequest() ends")
}
//main.swift - starts here
print("call sendRequest")
sendRequest()
call sendRequest
sendRequest called
sendRequest called: POST
keepRunning: true: loop
keepRunning: true: loop
....
keepRunning: true: loop
keepRunning: true: loop
keepRunning: true: loop
.....
keepRunning: true: loop
keepRunning: true: loop
sendRequest called: POST RETURNED Success
keepRunning: true: loop
keepRunning: true: loop
.....
keepRunning: true: loop
URL Session Task Succeeded: HTTP 200
keepRunning: true: loop
WS reponse handled set keepRunning to false
keepRunning: true: loop
keepRunning:false - sendRequest() ends
Program ended with exit code: 0
Si es solo para propósitos de prueba, puedes evitar el uso de semáforos si codificas el "tiempo de ejecución" de tu aplicación de línea de comandos de esta manera:
SWIFT 3
//put at the end of your main file
RunLoop.main.run(until: Date(timeIntervalSinceNow: 15)) //will run your app for 15 seconds only
Esta es una forma muy rápida y sucia de "habilitar" las aplicaciones de la línea de comandos para esperar a que finalicen otros subprocesos. Además, su aplicación se cerrará normalmente después de que expire el tiempo de espera, sin la necesidad de eliminar o cancelar explícitamente el proceso de la aplicación.
NOTA :
- Puede modificar el período de ''tiempo de espera'' si sus tareas de red requieren más tiempo para completarse.
- Esta ''solución'' es definitivamente una mala decisión si desea un mecanismo de espera más serio (también conocido como. NO USE ESTO EN LA PRODUCCIÓN)