Swift 上的同步 URL 请求 2
Synchronous URL request on Swift 2
我从 here 获得了这段代码,用于在 Swift 上同步请求 URL 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 弃用了唯一可以同步执行此操作的功能。
我该怎么做?
弃用是有原因的 - 它没有用处。您应该避免将同步网络请求视为瘟疫。它有两个主要问题,只有一个优点(它很容易使用..但不是异步的吗?):
- 如果不是从不同线程调用,请求会阻塞您的 UI,但如果您这样做,为什么不立即使用异步处理程序?
- 除非它自己出错,否则无法取消该请求
不用这个,只用异步请求:
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)
}
如果您真的想要同步进行,您可以随时使用信号量:
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: [=11=], encoding: .utf8) } ?? ""
f(reply)
}
同步请求有时在后台线程上运行良好。有时你有一个复杂的、不可能更改的充满异步请求的代码库,等等。然后有一个小请求不能作为异步折叠到当前系统中。如果同步失败,则您将得不到任何数据。简单的。它模仿文件系统的工作方式。
当然它没有涵盖所有类型的可能性,但是异步也有很多没有涵盖的可能性。
基于@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)
这可能会“卷曲”您的脚趾,但有时您只想:
- 跳过错误处理
- 真正做到同步,就像在测试中一样
try! String(contentsOf: URL(string: "https://google.com")!)
我从 here 获得了这段代码,用于在 Swift 上同步请求 URL 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 弃用了唯一可以同步执行此操作的功能。
我该怎么做?
弃用是有原因的 - 它没有用处。您应该避免将同步网络请求视为瘟疫。它有两个主要问题,只有一个优点(它很容易使用..但不是异步的吗?):
- 如果不是从不同线程调用,请求会阻塞您的 UI,但如果您这样做,为什么不立即使用异步处理程序?
- 除非它自己出错,否则无法取消该请求
不用这个,只用异步请求:
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)
}
如果您真的想要同步进行,您可以随时使用信号量:
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: [=11=], encoding: .utf8) } ?? ""
f(reply)
}
同步请求有时在后台线程上运行良好。有时你有一个复杂的、不可能更改的充满异步请求的代码库,等等。然后有一个小请求不能作为异步折叠到当前系统中。如果同步失败,则您将得不到任何数据。简单的。它模仿文件系统的工作方式。
当然它没有涵盖所有类型的可能性,但是异步也有很多没有涵盖的可能性。
基于@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)
这可能会“卷曲”您的脚趾,但有时您只想:
- 跳过错误处理
- 真正做到同步,就像在测试中一样
try! String(contentsOf: URL(string: "https://google.com")!)