Ios 如何在Swift中处理异步http请求

Ios 如何在Swift中处理异步http请求,ios,swift,Ios,Swift,我有以下代码: func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) { var bus = [String]() let headers = [ "content-type": "application/x-www-form-urlencoded", "cache-contro

我有以下代码:

    func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
    var bus = [String]()
    let headers = [
      "content-type": "application/x-www-form-urlencoded",
      "cache-control": "no-cache",
      "postman-token": "23cb4108-e24b-adab-b979-e37fd8f78622"
    ]

    let postData = NSMutableData(data: "bus_stop=Science Hill".data(using: String.Encoding.utf8)!)
    let request = NSMutableURLRequest(url: NSURL(string: "https://ucsc-bts3.soe.ucsc.edu/bus_stops/inner_eta.php?%22bus_stop%22=%22Science%20Hill%22")! as URL,
                                      cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
                                        timeoutInterval: 10.0)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    request.httpBody = postData as Data

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
        if (error != nil) {
            print(error!)
      } else {
            _ = response as? HTTPURLResponse
      }
        do {
            let jsonObject = try JSONSerialization.jsonObject(with: data!)
            guard let jsonArray = jsonObject as? [String: Any] else{
                print("JsonSerialization Failed")
                return
                }
            if let etaTableRows = jsonArray["rows"] as? NSArray{
                for etaData in etaTableRows{
                    let etaDictionary = etaData as? NSDictionary
                    bus.append(etaDictionary!["bus_type"] as! String)
                }
            }
        } catch {
            print("JSONSerialization error:", error)
        }
    })
    dataTask.resume()
    print(bus) //bus array should be updated here
    mapView.deselectAnnotation(annotation, animated: false)
    let schedule = ScheduleVC()
    schedule.data.append(annotation.title!! + " ETAs")
    self.present(schedule, animated: true, completion: nil)
}

在收到http响应并填充总线阵列之前,似乎正在运行打印(总线)。我的目标是用http响应数据填充总线阵列,然后打印它。我不知道如何做到这一点。

我建议您阅读《什么是
异步》

为了理解程序员的误解,让我们看看他是如何理解的 他认为密码是正确的。他认为代码的运行顺序如下:

您需要传递callback,并在
asynchronous
call中调用callback

例如:

class func getBusArray(completionHandler: (bus: NSArray) -> ()) {
    ...
    let task = session.dataTaskWithURL(url) {
        data, response, error in
        ...
        resultsArray = results
        completionHandler(bus: resultsArray)
    }
    ...
    task.resume()
}
你可以这样打电话

override func viewDidLoad() {
    MyModel.getBusArray {
        bus in
        println("Response: \(bus)")     
    }
}

我建议您阅读,什么是
异步

为了理解程序员的误解,让我们看看他是如何理解的 他认为密码是正确的。他认为代码的运行顺序如下:

您需要传递callback,并在
asynchronous
call中调用callback

例如:

class func getBusArray(completionHandler: (bus: NSArray) -> ()) {
    ...
    let task = session.dataTaskWithURL(url) {
        data, response, error in
        ...
        resultsArray = results
        completionHandler(bus: resultsArray)
    }
    ...
    task.resume()
}
你可以这样打电话

override func viewDidLoad() {
    MyModel.getBusArray {
        bus in
        println("Response: \(bus)")     
    }
}

在这种特殊情况下,解决方案非常简单: 在接收到闭包内的数据后运行代码

这很快:不要使用
NSURL
NSMutableData
NSMutableURLRequest
NSArray
NSDictionary

func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
    var bus = [String]()
    let headers = [
      "content-type": "application/x-www-form-urlencoded",
      "cache-control": "no-cache",
      "postman-token": "23cb4108-e24b-adab-b979-e37fd8f78622"
    ]

    let postData = Data("bus_stop=Science%20Hill".utf8)
    var request = URLRequest(url: URL(string: "https://ucsc-bts3.soe.ucsc.edu/bus_stops/inner_eta.php?%22bus_stop%22=%22Science%20Hill%22")!,
                                      cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
                                        timeoutInterval: 10.0)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    request.httpBody = postData

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request, completionHandler: { (data, response, error) in
        if let error = error {
            print(error); return
        }
        do {
           let jsonObject = try JSONSerialization.jsonObject(with: data!)
           guard let jsonArray = jsonObject as? [String: Any] else {
                print("JsonSerialization Failed")
                return
           }
           if let etaTableRows = jsonArray["rows"] as? [[String:Any]] {
                for etaData in etaTableRows {
                    bus.append(etaData["bus_type"] as! String)
                }
                print(bus)
                self.mapView.deselectAnnotation(annotation, animated: false)
                let schedule = ScheduleVC()
                schedule.data.append(annotation.title!! + " ETAs")
                self.present(schedule, animated: true, completion: nil)
          }
       } catch {
           print("JSONSerialization error:", error)
       }
    })
    dataTask.resume()
}

在这种特殊情况下,解决方案非常简单: 在接收到闭包内的数据后运行代码

这很快:不要使用
NSURL
NSMutableData
NSMutableURLRequest
NSArray
NSDictionary

func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
    var bus = [String]()
    let headers = [
      "content-type": "application/x-www-form-urlencoded",
      "cache-control": "no-cache",
      "postman-token": "23cb4108-e24b-adab-b979-e37fd8f78622"
    ]

    let postData = Data("bus_stop=Science%20Hill".utf8)
    var request = URLRequest(url: URL(string: "https://ucsc-bts3.soe.ucsc.edu/bus_stops/inner_eta.php?%22bus_stop%22=%22Science%20Hill%22")!,
                                      cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
                                        timeoutInterval: 10.0)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    request.httpBody = postData

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request, completionHandler: { (data, response, error) in
        if let error = error {
            print(error); return
        }
        do {
           let jsonObject = try JSONSerialization.jsonObject(with: data!)
           guard let jsonArray = jsonObject as? [String: Any] else {
                print("JsonSerialization Failed")
                return
           }
           if let etaTableRows = jsonArray["rows"] as? [[String:Any]] {
                for etaData in etaTableRows {
                    bus.append(etaData["bus_type"] as! String)
                }
                print(bus)
                self.mapView.deselectAnnotation(annotation, animated: false)
                let schedule = ScheduleVC()
                schedule.data.append(annotation.title!! + " ETAs")
                self.present(schedule, animated: true, completion: nil)
          }
       } catch {
           print("JSONSerialization error:", error)
       }
    })
    dataTask.resume()
}
一些意见:

  • 您应该将此网络代码拉入其自己的方法中
  • 您正在正文和URL中指定请求。身体足够了。将其从URL中删除
  • 您应该删除
    NSMutableData
    (使用
    Data
    )、
    NSMutableURLRequest
    (使用
    URLRequest
    )、
    NSURL
    (使用
    URL
    )、
    NSArray
    NSDictionary
    ,等等。通常最好尽可能保留在Swift类型中
  • 使用
    JSONDecoder
    而不是手动遍历
    JSONSerialization
    结果
  • 您还需要对请求正文进行百分比编码,因为
    bus_stop=Science Hill
    x-www-form-urlrequest
    中无效。看
  • 你说:

    我的目标是用http响应数据填充总线阵列,然后打印它

    您需要根据请求在完成处理程序闭包中移动代码

  • 因此:

    在哪里

    一些意见:

  • 您应该将此网络代码拉入其自己的方法中
  • 您正在正文和URL中指定请求。身体足够了。将其从URL中删除
  • 您应该删除
    NSMutableData
    (使用
    Data
    )、
    NSMutableURLRequest
    (使用
    URLRequest
    )、
    NSURL
    (使用
    URL
    )、
    NSArray
    NSDictionary
    ,等等。通常最好尽可能保留在Swift类型中
  • 使用
    JSONDecoder
    而不是手动遍历
    JSONSerialization
    结果
  • 您还需要对请求正文进行百分比编码,因为
    bus_stop=Science Hill
    x-www-form-urlrequest
    中无效。看
  • 你说:

    我的目标是用http响应数据填充总线阵列,然后打印它

    您需要根据请求在完成处理程序闭包中移动代码

  • 因此:

    在哪里

    extension Dictionary {
        func percentEncoded() -> Data? {
            return map { key, value in
                let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
                let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
                return escapedKey + "=" + escapedValue
            }
            .joined(separator: "&")
            .data(using: .utf8)
        }
    }
    
    extension CharacterSet {
        static let urlQueryValueAllowed: CharacterSet = {
            let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
            let subDelimitersToEncode = "!$&'()*+,;="
    
            var allowed = CharacterSet.urlQueryAllowed
            allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)
            return allowed
        }()
    }