同步 API 请求到异步 API 请求 Swift 2.2

Posted

技术标签:

【中文标题】同步 API 请求到异步 API 请求 Swift 2.2【英文标题】:Synchronous API request to Asynchronous API request Swift 2.2 【发布时间】:2016-11-12 10:25:42 【问题描述】:

嗯,我是 Swift 新手,对完成处理程序了解不多。我想从 API 获取请求并解析 JSON 响应,以便获取令牌。但是我的代码发生的情况是,每当我调用 getAuthentication 函数时,我的 UI 都会冻结并等待获取数据。这是getAuthentication的代码

func getAuthentication(username: String, password: String)
    let semaphore = dispatch_semaphore_create(0);
    let baseURL = "Some URL here"
    let url = NSURL(string: baseURL)!
    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"
    request.HTTPBody = "\n  \"username\": \"\(username)\",\n  \"password\": \"\(password)\"\n".dataUsingEncoding(NSUTF8StringEncoding);

    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request)  (data, response, error) -> Void in

        if error == nil
            let swiftyJSON = JSON(data: data!)
            print(swiftyJSON)

            //parse the data to get the user
            self.id = swiftyJSON["id"].intValue
            self.token = swiftyJSON["meta"]["token"].stringValue
         else 
            print("There was an error")
        
      dispatch_semaphore_signal(semaphore);
    
    task.resume()
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

然后,我在我的 LoginViewController 中调用此方法。有人说我正在使用同步请求,这就是我的 UI 冻结的原因,但我真的不知道如何将其更改为异步并等待下载数据。有人可以帮我弄这个吗?任何帮助将不胜感激。

【问题讨论】:

结帐Alamofire。这是一个执行异步 HTTP 请求的便捷框架。 干脆去掉信号量;你正在用它阻塞主线程。您需要在完成闭包中处理响应 @Reginald, dataTaskWithRequest 这里使用的NSURLSession 函数是一个异步函数。此处无需使用dispatch_queue。你可以看看我的回答。如有任何疑问,请随时发表评论 【参考方案1】:

首先,从你的函数中删除dispatch_semaphore相关代码。

func getAuthentication(username: String, password: String)

   let baseURL = "Some URL here"
   let url = NSURL(string: baseURL)!
   let request = NSMutableURLRequest(URL: url)
   request.HTTPMethod = "POST"
   request.HTTPBody = "\n  \"username\": \"\(username)\",\n  \"password\": \"\(password)\"\n".dataUsingEncoding(NSUTF8StringEncoding);

   let session = NSURLSession.sharedSession()
   let task = session.dataTaskWithRequest(request)  (data, response, error) -> Void in

       if error == nil
          let swiftyJSON = JSON(data: data!)
          print(swiftyJSON)

          //parse the data to get the user
          self.id = swiftyJSON["id"].intValue
          self.token = swiftyJSON["meta"]["token"].stringValue
        else 
          print("There was an error")
       
   
   task.resume()

在上面的代码中,函数dataTaskWithRequest本身就是一个异步函数。因此,您不需要在后台线程中调用函数getAuthentication

对于添加完成处理程序,

func getAuthentication(username: String, password: String, completion:((sucess: Bool) -> Void))

   let baseURL = "Some URL here"
   let url = NSURL(string: baseURL)!
   let request = NSMutableURLRequest(URL: url)
   request.HTTPMethod = "POST"
   request.HTTPBody = "\n  \"username\": \"\(username)\",\n  \"password\": \"\(password)\"\n".dataUsingEncoding(NSUTF8StringEncoding);

   let session = NSURLSession.sharedSession()
   let task = session.dataTaskWithRequest(request)  (data, response, error) -> Void in

       var successVal: Bool = true

       if error == nil
          let swiftyJSON = JSON(data: data!)
          print(swiftyJSON)
          self.id = swiftyJSON["id"].intValue
          self.token = swiftyJSON["meta"]["token"].stringValue
        else 
          print("There was an error")
          successVal = false
       

       dispatch_async(dispatch_get_main_queue(),  () -> Void in
          completion(successVal)                 
       )
   
   task.resume()

可以这样调用:

self.getAuthentication("user", password: "password", completion:  (success) -> Void in

)

【讨论】:

谢谢!这行得通!好吧,我忘记了 dispatch_async 这就是为什么即使我正在执行完成处理程序我也没有得到任何东西【参考方案2】:

您可以将转义的闭包参数传​​递给 getAuthentication 方法。

func getAuthentication(username: String, password: String, completion: (JSON) -> ())
    ...
    // create a request in the same way
    ...
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request)  (data, response, error) -> Void in
        if error == nil
            let swiftyJSON = JSON(data: data!)
            print(swiftyJSON)
            completion(swiftyJSON)
         else 
            print("There was an error")
        
    
    task.resume()

然后像这样在 LoginViewController 中调用 getAuthentication:

getAuthentication(username, password)  (json) -> in
    //Do whatever you want with the json result
    dispatch_async(dispatch_get_main_queue()) 
        // Do UI updates
    

另一种方法是在 LoginViewController 的后台线程中调用 getAuthentication 以避免阻塞主线程(即 UI 线程)。

//In LoginViewController   
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) 
      getAuthentication(username, password)
      dispatch_async(dispatch_get_main_queue()) 
         // UI updates
     

【讨论】:

以上是关于同步 API 请求到异步 API 请求 Swift 2.2的主要内容,如果未能解决你的问题,请参考以下文章

swift中具有多个闭包/ API请求的函数中的异步完成处理

如何在异步协程中包装同步函数?

angularjs怎么发异步请求

HTTP API 设计指南

架构设计|异步请求如何同步处理?

架构设计|异步请求如何同步处理?