tutorial programar lenguaje español desde descargar aprendiendo apple macos swift nsurlsession nsrunloop

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 :

  1. Puede modificar el período de ''tiempo de espera'' si sus tareas de red requieren más tiempo para completarse.
  2. 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)