ios - Xcode 中来自 API 的不稳定错误消息?
问题描述
当我尝试使用and处理decode
一些json
数据时,我遇到了一些不稳定的消息..为什么我说“不稳定的消息是有时当我点击请求时,我得到的是完整格式的响应,没有任何错误,但是经过几次运行投掷,这太令人困惑了......我也实现了,数据和格式也是正确的格式......谁能帮我解码这个...... ?Alamofire
PromiseKit
error
error
API
.GET
application
Xcode
Alamofire Response Serialization Error
Coding keys
json response
error message
在下一次运行之后,这里是error message
来自xcode console
:
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 = ""
}
}
}
和API
.GET
使用的请求Alamofire
:
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"
}
解决方案
如果在某些情况下,您的 API 应该返回数组,而是返回字典,您可以尝试重新实现responseDecodable(of:decoder:)
Alamofire 的便捷功能。
使用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)
}
}
}
}