【问题标题】:Unstable error message from the API in Xcode?Xcode 中来自 API 的不稳定错误消息?
【发布时间】:2021-02-01 16:11:47
【问题描述】:

当我尝试使用AlamofirePromiseKit 处理decode 一些json 数据时,我遇到了一些不稳定的error 消息.. 为什么我说“不稳定error 消息是有时当我点击了API.GET 请求,我得到了完整格式的响应,没有任何错误,但是在运行applicationXcode 几次后抛出了Alamofire Response Serialization Error,这太令人困惑了......我也实现了Coding keys,数据和json response 格式也是正确的格式.. 谁能帮我解码这个error message .?

这是我在随机应用程序运行之一中从API 得到的响应:

在下一次运行之后,这里是来自xcode consoleerror message

Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))))

如果需要对代码的任何部分进行更多分析,请告诉我,以便我用这些所需的代码更新问题内容.. .

这是我的json 响应模型结构:

// MARK: - TickerByPair
struct TickerByPair {
    var price, ask: Double
    var askVolume: Double
    var bid: Double
    var bidVolume, volume: Double
    var time: String
    
}

extension TickerByPair: Decodable{
    
    enum TrackCodingKeys: String, CodingKey {
        case price = "price"
        case ask = "ask"
        case askVolume = "askVolume"
        case bid = "bid"
        case bidVolume = "bidVolume"
        case volume = "volume"
        case time = "time"
    }
    
    
    
    init(from decoder: Decoder) throws {
        let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
        if trackContainer.contains(.price){
            price = try trackContainer.decode(Double.self, forKey: .price)
        }else{
            price = 0
        }
        if trackContainer.contains(.ask) {
            ask = try trackContainer.decode(Double.self, forKey: .ask)
        } else {
            ask = 0
        }
        if trackContainer.contains(.askVolume) {
            askVolume = try trackContainer.decode(Double.self, forKey: .askVolume)
        } else {
            askVolume = 0
        }
        if trackContainer.contains(.bid) {
            bid = try trackContainer.decode(Double.self, forKey: .bid)
        } else {
            bid = 0
        }
        if trackContainer.contains(.bidVolume) {
            bidVolume = try trackContainer.decode(Double.self, forKey: .bidVolume)
        } else {
            bidVolume = 0
        }
        if trackContainer.contains(.volume) {
            volume = try trackContainer.decode(Double.self, forKey: .volume)
        } else {
            volume = 0
        }
        if trackContainer.contains(.time) {
            time = try trackContainer.decode(String.self, forKey: .time)
        }
        else {
            time = ""
        }
    }
    
    
}


// MARK: - TradingPairElement
struct TradingPair {
    var id: Int
    var name: String
    var quoteAsset: String
    var baseAsset: String
}

extension TradingPair: Decodable {
    
    enum TrackCodingKeys: String, CodingKey {
        case id = "id"
        case name = "name"
        case quoteAsset = "quoteAsset"
        case baseAsset = "baseAsset"
       
    }
    init(from decoder: Decoder) throws {
        let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
        if trackContainer.contains(.id){
            id = try trackContainer.decode(Int.self, forKey: .id)
        }else{
            id = 0
        }
        if trackContainer.contains(.name) {
            name = try trackContainer.decode(String.self, forKey: .name)
        } else {
            name = ""
        }
        if trackContainer.contains(.quoteAsset) {
            quoteAsset = try trackContainer.decode(String.self, forKey: .quoteAsset)
        } else {
            quoteAsset = ""
        }
        if trackContainer.contains(.baseAsset) {
            baseAsset = try trackContainer.decode(String.self, forKey: .baseAsset)
        } else {
            baseAsset = ""
        }
    }
}

以及使用AlamofireAPI .GET 请求:


class ServerCommunicator {
    
    static func getAssets() -> Promise<[Assets]> {
        let decoder = JSONDecoder()
        return Promise { seal in
            AF.request(API.assets, method: .get, parameters: .none, headers: .none).responseDecodable(of: [Assets].self, decoder: decoder) { response in
                switch response.result {
                    case .success(let assets):
                        return seal.fulfill(assets)
                    case .failure(let error):
                        return seal.reject(error)
                }
            }
        }
    }
    
    static func getPairs() -> Promise<[TradingPair]> {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .useDefaultKeys
        return Promise { seal in
            AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseDecodable(of: [TradingPair].self, decoder: decoder) { response in
                switch response.result {
                    case .success(let pairs):
                        return seal.fulfill(pairs)
                    case .failure(let error):
                        return seal.reject(error)
                }
            }
        }
    }
    
    static func getPair(with pairName: String?) -> Promise<TickerByPair> {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .useDefaultKeys
        return Promise { seal in
            AF.request(API.getPairByTicker(pairName: pairName!), method: .get, parameters: .none, headers: .none).responseDecodable(of: TickerByPair.self, decoder: decoder) { response in
                switch response.result {
                    case .success(let ticker):
                        return seal.fulfill(ticker)
                    case .failure(let error):
                        return seal.reject(error)
                }
            }
        }
    }
}

而我的json response格式也如下:

[
  {
    "name": "ETH-KRW",
    "baseAsset": "ETH",
    "quoteAsset": "KRW"
  }, {
    "name": "BTC-KRW",
    "baseAsset": "BTC",
    "quoteAsset": "KRW"
  }, {
    "name": "BCH-KRW",
    "baseAsset": "BCH",
    "quoteAsset": "KRW"
  }
]

-------------

{
  "price": 10194500,
  "ask": 10195000,
  "bid": 10184500,
  "volume": 1752.05558316,
  "time": "2018-03-14T03:50:41.184Z"
}

【问题讨论】:

  • @gcharita,嗯,你能详细说明一下这个概念吗,我的意思是我如何识别和确认我的response 结构到任何一种类型..?
  • @gcharita,我的意思是,你认为我的responsejson data 还是json object..?
  • 首先,确定你的 API 是不是真的这么别扭。这意味着当您期望[Assets].self[TradingPair].self 时,您的API 会返回给您Assets.selfTradingPair.self。这是您的解码错误的根本原因。
  • @gcharita,是的,我实际上也认为这可能是原因,但是在使用邮递员进行测试时,没有出现错误,所以我认为问题的根本原因在于我自己的代码方法。 ...无论如何谢谢.. .

标签: ios json swift rest alamofire


【解决方案1】:

如果在某些情况下,您的 API 应该返回数组,而是返回字典,您可以尝试重新实现 Alamofire 方便的 responseDecodable(of:decoder:) 函数。

使用responseData(queue:completionHandler:) 并尝试将您的响应数据解码为数组,如果失败,请重试对象类型。

例如,您的 getPairs() 函数可能是这样的:

static func getPairs() -> Promise<[TradingPair]> {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .useDefaultKeys
    return Promise { seal in
        AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseData { response in
            switch response.result {
            case .success(let data):
                do {
                    let pairs = try decoder.decode([TradingPair].self, from: data)
                    return seal.fulfill(pairs)
                } catch DecodingError.typeMismatch {
                    do {
                        let pair = try decoder.decode(TradingPair.self, from: data)
                        return seal.fulfill([pair])
                    } catch {
                        return seal.reject(error)
                    }
                } catch {
                    return seal.reject(error)
                }
            case .failure(let error):
                return seal.reject(error)
            }
        }
    }
}

或者更一般地说,像这样扩展DataRequest

extension DataRequest {
    @discardableResult func responseArray<T: Decodable>(
        of type: T.Type,
        decoder: DataDecoder,
        completionHandler: @escaping (AFDataResponse<[T]>) -> Void
    ) -> Self {
        responseData { response in
            switch response.result {
            case .success(let data):
                do {
                    let array = try decoder.decode([T].self, from: data)
                    let dataResponse = self.dataResponse(from: response, result: .success(array))
                    completionHandler(dataResponse)
                } catch DecodingError.typeMismatch {
                    do {
                        let object = try decoder.decode(T.self, from: data)
                        let dataResponse = self.dataResponse(from: response, result: .success([object]))
                        completionHandler(dataResponse)
                    } catch {
                        let dataResponse: AFDataResponse<[T]> = self.dataResponse(
                            from: response,
                            result: .failure(.responseSerializationFailed(reason: .decodingFailed(error: error)))
                        )
                        completionHandler(dataResponse)
                    }
                } catch {
                    let dataResponse: AFDataResponse<[T]> = self.dataResponse(
                        from: response,
                        result: .failure(.responseSerializationFailed(reason: .decodingFailed(error: error)))
                    )
                    completionHandler(dataResponse)
                }
            case .failure(let error):
                let dataResponse: AFDataResponse<[T]> = self.dataResponse(from: response, result: .failure(error))
                completionHandler(dataResponse)
            }
        }
    }
    
    private func dataResponse<T: Decodable>(from response: AFDataResponse<Data>, result: Result<[T], AFError>) -> AFDataResponse<[T]> {
        return .init(
            request: response.request,
            response: response.response,
            data: response.data,
            metrics: response.metrics,
            serializationDuration: response.serializationDuration,
            result: result
        )
    }
}

然后你的getPairs() 函数会简单得多:

static func getPairs() -> Promise<[TradingPair]> {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .useDefaultKeys
    return Promise { seal in
        AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseArray(of: TradingPair.self, decoder: decoder) { response in
            switch response.result {
            case .success(let pairs):
                return seal.fulfill(pairs)
            case .failure(let error):
                return seal.reject(error)
            }
        }
    }
}

【讨论】:

  • 是的,这就是我猜的问题。我也修改了我的代码中的一些部分,但是在结合你建议的方法之后,我不再遇到那个愚蠢的错误了。我猜,谁做过API 只是做了一些可怕的配置错误.. 或 API 已过期.. 我也不太了解自己.. 无论如何,谢谢.. .)
猜你喜欢
  • 1970-01-01
  • 2015-05-08
  • 2013-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多