我可以通过Swift中的NSURLSession以某种方式执行同步HTTP请求

Fro*_*art 59 http nsurl httprequest swift

我可以通过NSURLSessionSwift 以某种方式执行同步HTTP请求吗?

我可以通过以下代码执行异步请求:

if let url = NSURL(string: "https://2ch.hk/b/threads.json") {
            let task = NSURLSession.sharedSession().dataTaskWithURL(url) {
                (data, response, error) in

                var jsonError: NSError?
                let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as [String: AnyObject]
                if jsonError != nil {
                    return
                }

                // ...
            }
            task.resume()
        }
Run Code Online (Sandbox Code Playgroud)

但同步请求怎么样?

提前致谢.

Nic*_*ets 68

您可以使用此NSURLSession扩展来添加同步方法:

extension NSURLSession {
    func synchronousDataTaskWithURL(url: NSURL) -> (NSData?, NSURLResponse?, NSError?) {
        var data: NSData?, response: NSURLResponse?, error: NSError?

        let semaphore = dispatch_semaphore_create(0)

        dataTaskWithURL(url) {
            data = $0; response = $1; error = $2
            dispatch_semaphore_signal(semaphore)
        }.resume()

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

        return (data, response, error)
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 3更新:

extension URLSession {
    func synchronousDataTask(with url: URL) -> (Data?, URLResponse?, Error?) {
        var data: Data?
        var response: URLResponse?
        var error: Error?

        let semaphore = DispatchSemaphore(value: 0)

        let dataTask = self.dataTask(with: url) {
            data = $0
            response = $1
            error = $2

            semaphore.signal()
        }
        dataTask.resume()

        _ = semaphore.wait(timeout: .distantFuture)

        return (data, response, error)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我将这个扩展名添加到我的文件的底部,并将`NSURLSession.sharedSession().dataTaskWithURL(url)`替换为`NSURLSession.sharedSession().synchronous synchronousDataTaskWithURL(url)`,但是我收到一个错误,即有一个额外的电话中的争论.调用此扩展功能的正确方法是什么? (2认同)
  • 你写的是调用它的正确方法,可能错误在其他地方.你能提供更多背景吗? (2认同)
  • 额外的参数是完成处理程序,不再需要在同步调用中进行参数化.调用此方法应如下所示(假设您已声明所需的变量):`(data,response,error)= NSURLSession.sharedSession().synchronousizationDataTaskWithURL(url)` (2认同)
  • 当我在自己的解决方案中实现它时,我的完成处理程序永远不会被调 当我打开CFNetworkingDiagnostics时,我可以看到URL请求成功完成,但我的处理程序从未执行过.任何人都可以体验这一点,或者有任何解决方案的指导?谢谢. (2认同)

Uma*_*que 37

Apple线程讨论同样的问题.

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request  
    returningResponse:(__autoreleasing NSURLResponse **)responsePtr  
    error:(__autoreleasing NSError **)errorPtr {  
    dispatch_semaphore_t    sem;  
    __block NSData *        result;  

    result = nil;  

    sem = dispatch_semaphore_create(0);  

    [[[NSURLSession sharedSession] dataTaskWithRequest:request  
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {  
        if (errorPtr != NULL) {  
            *errorPtr = error;  
        }  
        if (responsePtr != NULL) {  
            *responsePtr = response;  
        }  
        if (error == nil) {  
            result = data;  
        }  
        dispatch_semaphore_signal(sem);  
    }] resume];  

    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);  

   return result;  
}  
Run Code Online (Sandbox Code Playgroud)

通过回答Quinn"The Eskimo!" Apple Developer Relations,开发人员技术支持,核心操作系统/硬件


Jon*_*nny 15

更新了其中一个使用URLRequest的答案,因此我们可以使用PUT等.

extension URLSession {
    func synchronousDataTask(urlrequest: URLRequest) -> (data: Data?, response: URLResponse?, error: Error?) {
        var data: Data?
        var response: URLResponse?
        var error: Error?

        let semaphore = DispatchSemaphore(value: 0)

        let dataTask = self.dataTask(with: urlrequest) {
            data = $0
            response = $1
            error = $2

            semaphore.signal()
        }
        dataTask.resume()

        _ = semaphore.wait(timeout: .distantFuture)

        return (data, response, error)
    }
}
Run Code Online (Sandbox Code Playgroud)

我这样打电话.

var request = URLRequest(url: url1)
request.httpBody = body
request.httpMethod = "PUT"
let (_, _, error) = URLSession.shared.synchronousDataTask(urlrequest: request)
if let error = error {
    print("Synchronous task ended with error: \(error)")
}
else {
    print("Synchronous task ended without errors.")
}
Run Code Online (Sandbox Code Playgroud)


dro*_*dev 9

我想使用提供更现代的解决方案DispatchGroup

使用示例1:

var urlRequest = URLRequest(url: config.pullUpdatesURL)
urlRequest.httpMethod = "GET"
urlRequest.httpBody = requestData
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

let (data, response, error) = URLSession.shared.syncRequest(with: urlRequest)
Run Code Online (Sandbox Code Playgroud)

使用示例2:

let url = URL(string: "https://www.google.com/")
let (data, response, error) = URLSession.shared.syncRequest(with: url)
Run Code Online (Sandbox Code Playgroud)

扩展代码:

extension URLSession {
   
   func syncRequest(with url: URL) -> (Data?, URLResponse?, Error?) {
      var data: Data?
      var response: URLResponse?
      var error: Error?
      
      let dispatchGroup = DispatchGroup()
      let task = dataTask(with: url) {
         data = $0
         response = $1
         error = $2
         dispatchGroup.leave()
      }
      dispatchGroup.enter()
      task.resume()
      dispatchGroup.wait()
      
      return (data, response, error)
   }
   
   func syncRequest(with request: URLRequest) -> (Data?, URLResponse?, Error?) {
      var data: Data?
      var response: URLResponse?
      var error: Error?
      
      let dispatchGroup = DispatchGroup()
      let task = dataTask(with: request) {
         data = $0
         response = $1
         error = $2
         dispatchGroup.leave()
      }
      dispatchGroup.enter()
      task.resume()
      dispatchGroup.wait()
      
      return (data, response, error)
   }
   
}
Run Code Online (Sandbox Code Playgroud)

作为奖励,如果需要,您可以轻松实现超时。为此,您需要使用

func wait(timeout: DispatchTime) -> DispatchTimeoutResult代替 func wait()


e a*_*e a -3

请小心同步请求,因为它可能会导致糟糕的用户体验,但我知道有时这是必要的。对于同步请求,请使用 NSURLConnection:

func synchronousRequest() -> NSDictionary {

        //creating the request
        let url: NSURL! = NSURL(string: "exampledomain/...")
        var request = NSMutableURLRequest(URL: url)
        request.HTTPMethod = "GET"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")


        var error: NSError?

        var response: NSURLResponse?

        let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)

        error = nil
        let resultDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(urlData!, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary

        return resultDictionary
    }
Run Code Online (Sandbox Code Playgroud)

  • 这不再适用于 Swift 2。苹果显然决定我们不需要同步请求。 (2认同)