我有以下代码:
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)
}
看来 print(bus) 在收到 http 响应并填充总线数组之前正在运行。我的目标是用 http 响应数据填充总线数组,然后打印它。我不知道如何实现这一点。
最佳答案
一些观察:
- 您应该将此网络代码拉入其自己的方法中。
- 您在正文和 URL 中指定请求。 body 就够了。从 URL 中删除它。
- 您应该删除
NSMutableData
(使用Data
),NSMutableURLRequest
(使用URLRequest
),NSURL
(使用URL
),NSArray
,NSDictionary
等等。通常最好尽可能保留 Swift 类型。 - 而不是手动迭代
JSONSerialization
结果,使用JSONDecoder
. - 您还需要对请求正文进行百分比编码,如
bus_stop=Science Hill
在x-www-form-urlrequest
中无效。请参阅https://stackoverflow.com/a/26365148/1271826 . 你说:
My goal would be to fill the bus array with the http response data, then print it
您需要根据完成处理程序闭包内的请求移动代码。
因此:
func fetchBuses(completion: @escaping (Result<[Bus], Error>) -> Void) {
let headers = [
"content-type": "application/x-www-form-urlencoded",
"cache-control": "no-cache",
"postman-token": "23cb4108-e24b-adab-b979-e37fd8f78622"
]
let url = URL(string: "https://ucsc-bts3.soe.ucsc.edu/bus_stops/inner_eta.php")!
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10)
request.httpBody = ["bus_stop": "Science Hill"].percentEncoded()
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request) { data, response, error in
guard
error == nil,
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
completion(.failure(error ?? BusError.unknown(data, response)))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let responseObject = try decoder.decode(ResponseObject.self, from: responseData)
completion(.success(responseObject.rows))
} catch let jsonError {
completion(.failure(jsonError))
}
}
dataTask.resume()
}
还有
func mapView(_ mapView: MGLMapView, annotation: MGLAnnotation, calloutAccessoryControlTapped control: UIControl) {
fetchBuses { result in
switch result {
case .failure(let error):
print(error)
case .success(let buses):
print(buses)
DispatchQueue.main.async {
mapView.deselectAnnotation(annotation, animated: false)
let schedule = ScheduleVC()
schedule.data.append(annotation.title!! + " ETAs")
self.present(schedule, animated: true, completion: nil)
}
}
}
}
哪里
enum BusError: Error {
case unknown(Data?, URLResponse?)
}
struct Bus: Decodable {
let busId: Int
let busType: String
let nextBusStop: String
let timeAway: Int
}
struct ResponseObject: Decodable {
let rows: [Bus]
}
还有
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
}()
}
关于ios - 如何在 Swift 中处理异步 http 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60458251/