来自 Xcode 中的 API 的不稳定错误消息?
Unstable error message from the API in Xcode?
当我尝试使用 Alamofire
和 PromiseKit
decode
一些 json
数据时,我遇到了一些不稳定的 error
消息..为什么我我说“不稳定 error
消息是有时当我点击 API
.GET
请求时,我得到完整格式的响应,没有任何错误,但在几次之后 运行宁 application
Xcode
抛出一个 Alamofire Response Serialization Error
这太令人困惑了......我也实现了 Coding keys
,数据和 json response
格式也在正确的格式.. 谁能帮我解码这个 error message
.?
这是我在随机应用程序 运行 中从 API
得到的响应:
在下一个 运行 之后,这里是 xcode console
中的 error 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 = ""
}
}
}
和 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 应该是 return 数组,而不是 return 字典,你可以尝试 re-implementing 方便的 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)
}
}
}
}
当我尝试使用 Alamofire
和 PromiseKit
decode
一些 json
数据时,我遇到了一些不稳定的 error
消息..为什么我我说“不稳定 error
消息是有时当我点击 API
.GET
请求时,我得到完整格式的响应,没有任何错误,但在几次之后 运行宁 application
Xcode
抛出一个 Alamofire Response Serialization Error
这太令人困惑了......我也实现了 Coding keys
,数据和 json response
格式也在正确的格式.. 谁能帮我解码这个 error message
.?
这是我在随机应用程序 运行 中从 API
得到的响应:
在下一个 运行 之后,这里是 xcode console
中的 error 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 = ""
}
}
}
和 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 应该是 return 数组,而不是 return 字典,你可以尝试 re-implementing 方便的 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)
}
}
}
}