Синхронный запрос URL на Swift 2
У меня есть этот код здесь, чтобы выполнить синхронный запрос URL-адреса на Swift 2.
func send(url: String, f: (String)-> ()) {
var request = NSURLRequest(URL: NSURL(string: url)!)
var response: NSURLResponse?
var error: NSErrorPointer = nil
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)
var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
f(reply)
}
но функция NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)
была устаревшей, и я не вижу, как можно выполнять синхронные запросы в Swift, потому что альтернатива является асинхронной. Очевидно, Apple отказалась от единственной функции, которая может делать это синхронно.
Как я могу это сделать?
Ответы
Ответ 1
Есть причина, по которой усталость - для нее просто бесполезно. Вы должны избегать синхронных сетевых запросов как чума. У этого есть две основные проблемы и только одно преимущество (это легко использовать.. но не асинк также?):
- Запрос блокирует ваш пользовательский интерфейс, если он не вызывается из другого потока, но если вы это сделаете, почему бы не использовать асинхронный обработчик сразу?
- Невозможно отменить этот запрос, кроме случаев, когда он сам по себе
Вместо этого просто используйте асинхронный запрос:
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
// Handle incoming data like you would in synchronous request
var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
f(reply)
})
iOS9 Устаревание
Так как в iOS9 этот метод устарел, я предлагаю вам вместо этого использовать NSURLSession:
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
// Handle incoming data like you would in synchronous request
var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
f(reply)
}
Ответ 2
Если вы действительно хотите сделать это синхронно, вы всегда можете использовать семафор:
func send(url: String, f: (String) -> Void) {
var request = NSURLRequest(URL: NSURL(string: url)!)
var error: NSErrorPointer = nil
var data: NSData
var semaphore = dispatch_semaphore_create(0)
try! NSURLSession.sharedSession().dataTaskWithRequest(request) { (responseData, _, _) -> Void in
data = responseData! //treat optionals properly
dispatch_semaphore_signal(semaphore)
}.resume()
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
f(reply)
}
РЕДАКТИРОВАТЬ: Добавьте некоторых хакеров! поэтому код работает, не делайте этого в производственном коде
Swift 3.0+ (3.0, 3.1, 3.2, 4.0)
func send(url: String, f: (String) -> Void) {
guard let url = URL(string: url) else {
print("Error! Invalid URL!") //Do something else
return
}
let request = URLRequest(url: url)
let semaphore = DispatchSemaphore(value: 0)
var data: Data? = nil
URLSession.shared.dataTask(with: request) { (responseData, _, _) -> Void in
data = responseData
semaphore.signal()
}.resume()
semaphore.wait(timeout: .distantFuture)
let reply = data.flatMap { String(data: $0, encoding: .utf8) } ?? ""
f(reply)
}
Ответ 3
На основании ответа @fpg1503 я сделал простое расширение в Swift 3:
extension URLSession {
func synchronousDataTask(with request: URLRequest) throws -> (data: Data?, response: HTTPURLResponse?) {
let semaphore = DispatchSemaphore(value: 0)
var responseData: Data?
var theResponse: URLResponse?
var theError: Error?
dataTask(with: request) { (data, response, error) -> Void in
responseData = data
theResponse = response
theError = error
semaphore.signal()
}.resume()
_ = semaphore.wait(timeout: .distantFuture)
if let error = theError {
throw error
}
return (data: responseData, response: theResponse as! HTTPURLResponse?)
}
}
Затем вы просто вызываете:
let (data, response) = try URLSession.shared.synchronousDataTask(with: request)
Ответ 4
Синхронные запросы иногда отлично подходят для фоновых потоков. Иногда у вас сложная, невозможная замена базы кода, заполненной асинхронными запросами, и т.д. Тогда есть небольшой запрос, который не может быть сложен в текущую систему как async. Если синхронизация не удалась, вы не получите никаких данных. Просто. Он имитирует работу файловой системы.
Уверен, что он не охватывает всевозможные события, но есть много вариантов, которые не рассматриваются в асинхронном режиме.