Swift:解码API响应时得到零

问题描述

我在解码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 (将#修改为@)