Использование NSURLSession из программы командной строки Swift
Я пытаюсь протестировать небольшое приложение командной строки для проверки концепции, прежде чем интегрировать его в более крупное приложение. То, что я пытаюсь сделать, это загрузить некоторые данные, используя NSURLSession, используя этот пример. Однако, похоже, что если я использую примеры, приведенные в простом приложении командной строки OS X, то приложение завершается до получения данных.
Как я могу загрузить данные из автономного приложения командной строки, используя NSURLSession? То, о чем я читал, - это использование NSRunLoop, однако я еще не нашел четкого примера в Swift, так что, если NSRunLoop действительно является подходящим вариантом, то любые примеры будут оценены.
Любые другие стратегии загрузки данных из URL для приложения командной строки Swift также приветствуются (бесконечный цикл while?).
4 ответа
Вы можете использовать семафор, чтобы заблокировать текущий поток и дождаться окончания сеанса URL.
Создайте семафор, начните сеанс URL, затем дождитесь семафора. От вашего обратного вызова завершения сеанса URL, сигнализируйте семафор.
Вы можете использовать глобальный флаг (объявить переменную логической переменной) и опрашивать его из цикла while, но это менее оптимально. Во-первых, вы сжигаете циклы процессора без необходимости.
Вот быстрый пример, который я сделал на детской площадке:
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()
Попробуй это
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
Если это только для целей тестирования, вы можете избежать использования семафора, если жестко запрограммируете "время выполнения" приложения командной строки, например:
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
Это очень быстрый и грязный способ "включить" приложения командной строки для ожидания завершения других потоков. Кроме того, ваше приложение будет нормально завершать работу после истечения времени ожидания без необходимости явного уничтожения или отмены процесса приложения.
ПРИМЕЧАНИЕ:
- Вы можете изменить период ожидания, если сетевым задачам требуется больше времени для выполнения.
- Это "решение" - определенно плохое решение, если вам нужен более серьезный механизм ожидания (иначе. НЕ ИСПОЛЬЗУЙТЕ ЭТО В ПРОИЗВОДСТВЕ)
Я обошел это с помощью цикла while и флага bool, который устанавливается в true, когда сеанс URL возвращается
Xcode > MacOS > консольное приложение
main.swift
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