ios Swift 2 上的同步 URL 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31557688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:57:19  来源:igfitidea点击:

Synchronous URL request on Swift 2

iosiphoneswift2ios9

提问by DuckDucking

I have this code from hereto do synchronous request of a URL on Swift 2.

我有这个代码从这里在 Swift 2 上做一个 URL 的同步请求。

  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)
  }

but the function NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)was deprecated and I don't see how one can do synchronous requests on Swift, cause the alternative is asynchronous. Apparently Apple deprecated the only function that can do it synchronously.

但该函数NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)已被弃用,我不知道如何在 Swift 上执行同步请求,因为替代方案是异步的。显然,Apple 弃用了唯一可以同步执行的功能。

How can I do that?

我怎样才能做到这一点?

采纳答案by Jiri Trecak

There is a reason behind deprecation - there is just no use for it. You should avoid synchronous network requests as a plague. It has two main problems and only one advantage (it is easy to use.. but isn't async as well?):

弃用背后有一个原因 - 它没有用。您应该避免将同步网络请求视为瘟疫。它有两个主要问题,只有一个优点(它很容易使用......但不是异步的吗?):

  • The request blocks your UI if not called from different thread, but if you do that, why don't use asynchronous handler right away?
  • There is no way how to cancel that request except when it errors on its own
  • 如果不是从不同的线程调用,请求会阻塞你的 UI,但如果你这样做,为什么不立即使用异步处理程序?
  • 除非它自己出错,否则无法取消该请求

Instead of this, just use asynchronous request:

而不是这样,只需使用异步请求:

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 Deprecation

iOS9 弃用

Since in iOS9 this method is being deprecated, I suggest you to use NSURLSession instead:

由于在 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)
}

回答by fpg1503

If you reallywanna do it synchronously you can always use a semaphore:

如果你真的想同步做,你总是可以使用信号量:

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)
}

EDIT:Add some hackish ! so the code works, don't do this in production code

编辑:添加一些hackish!所以代码有效,不要在生产代码中这样做

Swift 3.0+ (3.0, 3.1, 3.2, 4.0)

斯威夫特 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: 
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?)

    }

}
, encoding: .utf8) } ?? "" f(reply) }

回答by Tom Andersen

Synchronous requests are sometimes fine on background threads. Sometimes you have a complicated, impossible to change code base full of async requests, etc. Then there is a small request that can't be folded into the current system as async. If the sync fails, then you get no data. Simple. It mimics how the file system works.

同步请求有时在后台线程上很好。有时你有一个复杂的、不可能改变的代码库,里面充满了异步请求等等。然后有一个小请求不能作为异步折叠到当前系统中。如果同步失败,则您不会获得任何数据。简单的。它模仿文件系统的工作方式。

Sure it does not cover all sorts of eventualities, but there are lots of eventualities not covered in async as well.

当然,它并没有涵盖所有可能发生的情况,但是异步中也没有涵盖很多可能发生的情况。

回答by Matej Ukmar

Based on @fpg1503 answer I made a simple extension in Swift 3:

基于@fpg1503 的回答,我在 Swift 3 中做了一个简单的扩展:

let (data, response) = try URLSession.shared.synchronousDataTask(with: request)

Then you simply call:

然后你只需调用:

##代码##