Swift 2上的同步URL请求

Spa*_*Dog 17 iphone ios ios9 swift2

我从这里有这个代码在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)
  }
Run Code Online (Sandbox Code Playgroud)

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

我怎样才能做到这一点?

fpg*_*503 39

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

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)
}
Run Code Online (Sandbox Code Playgroud)

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

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)
}
Run Code Online (Sandbox Code Playgroud)


Jir*_*cak 10

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

  • 如果没有从不同的线程调用,请求会阻止您的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)
})
Run Code Online (Sandbox Code Playgroud)

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)
}
Run Code Online (Sandbox Code Playgroud)

  • 我觉得,即使你是正确的,问题是寻找一种做同步网络的方法,这不是一个答案. (16认同)
  • 如果您需要在 NSOperation 的 main 方法中进行网络调用怎么办?这就是同步网络的合理性所在。 (2认同)

Tom*_*sen 9

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

当然它并没有涵盖各种各样的可能性,但是同样存在许多不可能的事件.


Mat*_*mar 9

根据@ 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?)

    }

}
Run Code Online (Sandbox Code Playgroud)

然后你只需致电:

let (data, response) = try URLSession.shared.synchronousDataTask(with: request)
Run Code Online (Sandbox Code Playgroud)