问题描述
我在解码API响应时遇到问题。
因此,我们有一个NetworkManager类,可用于解码API。我有一个简单的GET端点,需要从中检索机场列表。这是端点:
static let airports = Endpoint(url: "/test/airports")
端点定义如下:
public struct Endpoint : Equatable {
public init(url: String? = nil,pattern: String? = nil,methods: [Test.HTTPMethod] = [.get],type: Test.EncodingType = .json)
}
然后在我们的网络经理中,我们拥有:
public func call<R: Decodable>(_ endpoint: Endpoint,with args: [String: String]? = nil,using method: HTTPMethod = .get,expecting response: R.Type?,completion: APIResponse<R>) {
call(endpoint,with: args,parameters: nothing(),using: method,posting: nothing(),expecting: response,completion: completion)
}
我的机场模型如下:
struct Airport: Codable {
let id: String
let name: String
let iata3: String
let icao4: String
let countryCode: String
}
然后我像这样呼叫端点:
private func getAirportsList() {
API.client.call(.airports,expecting: [Airport].self) { (result,airports) in
print(airports)
}
}
现在,我正在使用Charles代理,并且得到了期望的响应:
[{
"id": "5f92b0269c983567fc4b9683","name": "Amsterdam Schiphol","iata3": "AMS","icao4": "EHAM","countryCode": "NL"
},{
"id": "5f92b0269c983567fc4b9685","name": "Bahrain International","iata3": "BAH","icao4": "OBBI","countryCode": "BH"
},{
"id": "5f92b0269c983567fc4b968b","name": "Bankstown","iata3": "BWU","icao4": "YSBK","countryCode": "AU"
}]
但是在我的getAirports()方法中,机场为零。我真的很想知道为什么。显然,端点被正确命中,但我的解码失败。
编辑:
完整方法:
private func call<P: encodable,B: encodable,R: Decodable>(_ endpoint: Endpoint,parameters params: P?,posting body: B?,expecting responseType: R.Type?,completion: APIResponse<R>) {
// Prepare our URL components
guard var urlComponents = URLComponents(string: baseURL.absoluteString) else {
completion?(.failure(nil,NetworkError(reason: .invalidURL)),nil)
return
}
guard let endpointPath = endpoint.url(with: args) else {
completion?(.failure(nil,nil)
return
}
urlComponents.path = urlComponents.path.appending(endpointPath)
// Apply our parameters
applyParameters: if let parameters = try? params.asDictionary() {
if parameters.count == 0 {
break applyParameters
}
var queryItems = [URLQueryItem]()
for (key,value) in parameters {
if let value = value as? String {
let queryItem = URLQueryItem(name: key,value: value)
queryItems.append(queryItem)
}
}
urlComponents.queryItems = queryItems
}
// Try to build the URL,bad request if we can't
guard let urlString = urlComponents.url?.absoluteString.removingPercentEncoding,var url = URL(string: urlString) else {
completion?(.failure(nil,nil)
return
}
if let uuid = UIDevice.current.identifierForvendor?.uuidString,endpoint.pattern == "/logging/v1/device/<device_id>" {
let us = "http://192.168.6.128:3000/logging/v1/device/\(uuid)"
guard let u = URL(string: us) else { return }
url = u
}
// Can we call this method on this endpoint? If not,lets not try to continue
guard endpoint.httpMethods.contains(method) else {
completion?(.failure(nil,NetworkError(reason: .methodNotAllowed)),nil)
return
}
// Apply debug cookie
if let debugCookie = debugCookie {
httpcookiestorage.shared.setCookies(
HTTPCookie.cookies(
withResponseHeaderFields: ["Set-Cookie": debugCookie],for:url
),for: url,mainDocumentURL: url)
}
// Build our request
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
if let headers = headers {
for (key,value) in headers {
request.setValue(value,forHTTPHeaderField: key)
}
}
// If we are posting,safely retrieve the body and try to assign it to our request
if !(body is nothingProtocol) {
guard let body = body else {
completion?(.failure(nil,NetworkError(reason: .buildingPayload)),nil)
return
}
do {
let result = try encode(body: body,type: endpoint.encodingType)
request.httpBody = result.data
request.setValue(result.headerValue,forHTTPHeaderField: "Content-Type")
} catch {
completion?(.failure(nil,nil)
return
}
}
// Build our response handler
let task = session.dataTask(with: request as URLRequest) { (rawData,response,error) in
// Print some logs to help track requests
var debugOutput = "URL\n\(url)\n\n"
if !(params is nothing.Type) {
debugOutput.append(contentsOf: "ParaMETERS\n\(params.asJSONString() ?? "No Parameters")\n\n")
}
if !(body is nothing.Type) {
debugOutput.append(contentsOf: "BODY\n\(body.asJSONString() ?? "No Body")\n\n")
}
if let responseData = rawData {
debugOutput.append(contentsOf: "RESPONSE\n\(String(data: responseData,encoding: .utf8) ?? "No Response Content")")
}
Logging.client.record(debugOutput,domain: .network,level: .debug)
guard let httpResponse = response as? HTTPURLResponse else {
guard error == nil else {
completion?(.failure(nil,NetworkError(reason: .unwrappingResponse)),nil)
return
}
completion?(.failure(nil,NetworkError(reason: .invalidResponseType)),nil)
return
}
let statusCode = httpResponse.statusCode
// We have an error,return it
guard error == nil,NetworkManager.successstatusRange.contains(statusCode) else {
var output: Any?
if let data = rawData {
output = (try? JSONSerialization.jsonObject(with: data,options: .allowFragments)) ?? "Unable to connect"
Logging.client.record("Response: \(String(data: data,encoding: .utf8) ?? "No error data")",domain: .network)
}
completion?(.failure(statusCode,NetworkError(reason: .requestFailed,json: output)),nil)
return
}
// Safely cast the responseType we are expecting
guard let responseType = responseType else {
completion?(.failure(statusCode,NetworkError(reason: .castingToExpectedType)),nil)
return
}
// If we are expecting nothing,return Now (since we will have nothing!)
if responseType is nothing.Type {
completion?(.success(statusCode),nil)
return
}
guard let data = rawData else {
assertionFailure("Could not cast data from payload when we passed pre-cast checks")
return
}
// Decode the JSON and cast to our expected response type
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let responSEObject = try decoder.decode(responseType,from: data)
completion?(.success(statusCode),responSEObject)
return
} catch let error {
let content = try? JSONSerialization.jsonObject(with: data,options: .allowFragments)
Logging.client.record("Failed to build codable from JSON: \(String(describing: content))\n\nError: \(error)",level: .error)
assertionFailure("Failed to build codable from JSON: \(error)")
completion?(.failure(statusCode,nil)
return
}
}
// Submit our request
task.resume()
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)