generics - 如何使用 Codable 解析通用响应
问题描述
每个 API 在响应中都有三个参数。
Code
: 指示 API 是成功还是失败(1 或 0)Message
: 一个字符串Data
: 可以是Array
对象或单个对象。
我已经创建了基本模型。
struct ResponseBase<T:Codable> : Codable {
let code : String?
let data : [T]
let message : String?
enum CodingKeys: String, CodingKey {
case code = "Code"
case data = "Data"
case message = "Message"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
code = try values.decodeIfPresent(String.self, forKey: .code)
data = try values.decodeIfPresent([T].self, forKey: .data)
message = try values.decodeIfPresent(String.self, forKey: .message)
}
}
struct SocialWarmer : Codable {
let createdDate : String?
let lookUpId : String?
let lookupKey : String?
let lookupValue : String?
let parentId : String?
let statusFlag : String?
let type : String?
let updatedDate : String?
enum CodingKeys: String, CodingKey {
case createdDate = "CreatedDate"
case lookUpId = "LookUpId"
case lookupKey = "LookupKey"
case lookupValue = "LookupValue"
case parentId = "ParentId"
case statusFlag = "StatusFlag"
case type = "Type"
case updatedDate = "UpdatedDate"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
createdDate = try values.decodeIfPresent(String.self, forKey: .createdDate)
lookUpId = try values.decodeIfPresent(String.self, forKey: .lookUpId)
lookupKey = try values.decodeIfPresent(String.self, forKey: .lookupKey)
lookupValue = try values.decodeIfPresent(String.self, forKey: .lookupValue)
parentId = try values.decodeIfPresent(String.self, forKey: .parentId)
statusFlag = try values.decodeIfPresent(String.self, forKey: .statusFlag)
type = try values.decodeIfPresent(String.self, forKey: .type)
updatedDate = try values.decodeIfPresent(String.self, forKey: .updatedDate)
}
}
以下是 API 请求的代码。
class BaseApiClient {
static let `default` = BaseApiClient()
private init() {
}
func fetch<model:Codable>(request:APIRouter,decoder : JSONDecoder = JSONDecoder() ,onSuccess: @escaping ([model]) -> Void) {
if Connectivity.isReachable {
(UIApplication.shared.delegate as! AppDelegate).addProgressView()
Alamofire.request(request).responseJSON { (response) in
switch response.result {
case .success( let apiResponse) :
DispatchQueue.main.async {
(UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
}
if let responseData = apiResponse as? [String:Any] , let status = responseData["Code"] as? String , status == "SUCCESS" {
do {
let responseModel = try decoder.decode(ResponseBase<model>.self, from: response.data!)
onSuccess(responseModel.data!)
}
catch let error as NSError {
print("failed reason : \(error.localizedDescription)")
}
print(model.Type.self)
print(model.self)
}
else {
UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: "Service not Avilabel" ,okclick: nil)
}
case .failure(let error) :
UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: error.localizedDescription, okclick: nil)
}
}
}
else {
(UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Error", message: "connnection not avilabel", okclick: nil)
}
}
}
以下是调用 API 的代码。
BaseApiClient.default.fetch(request: APIRouter.GetSocialWarmerType) { (response: [SocialWarmer]) in
print(response)
}
但是,如果数据是单个对象,则此模型和 API 方法将不起作用。我想要实现的是在 API 方法中创建单个模型和适当的更改,以解析对象数组和单个对象。
解决方案
最后我找到了一个解决方法。我创建了两个基类,一个用于对象数组,一个用于单个对象。
struct ResponseBaseArray<T:Codable> : Codable {
let code : String?
let data : [T]?
let message : String?
enum CodingKeys: String, CodingKey {
case code = "Code"
case data = "Data"
case message = "Message"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
code = try values.decodeIfPresent(String.self, forKey: .code)
data = try values.decodeIfPresent([T].self, forKey: .data)
message = try values.decodeIfPresent(String.self, forKey: .message)
}
}
struct ResponseBaseObject<T:Codable> : Codable {
let code : String?
let data : T?
let message : String?
enum CodingKeys: String, CodingKey {
case code = "Code"
case data = "Data"
case message = "Message"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
code = try values.decodeIfPresent(String.self, forKey: .code)
data = try values.decodeIfPresent(T.self, forKey: .data)
message = try values.decodeIfPresent(String.self, forKey: .message)
}
}
以下是 Api 方法的代码。
class BaseApiClient {
static let `default` = BaseApiClient()
private init() {
}
func fetch<model:Codable>(request:APIRouter , decoder: JSONDecoder = JSONDecoder() ,onSuccess: @escaping (model) -> Void) {
if Connectivity.isReachable {
(UIApplication.shared.delegate as! AppDelegate).addProgressView()
Alamofire.request(request).responseJSON { (response) in
switch response.result {
case .success( let apiResponse) :
DispatchQueue.main.async {
(UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
}
if let responseData = apiResponse as? [String:Any] , let status = responseData["Code"] as? String , status == "SUCCESS" {
do {
let responseModel = try decoder.decode(model.self, from: response.data!)
onSuccess(responseModel)
}
catch let error as NSError {
print("failed reason : \(error.localizedDescription)")
}
}
else {
UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: "Service not Avilabel" ,okclick: nil)
}
case .failure(let error) :
UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: error.localizedDescription, okclick: nil)
}
}
}
else {
(UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Error", message: "connnection not avilabel", okclick: nil)
}
}
}
以下是调用 API 的代码。
BaseApiClient.default.fetch(request: APIRouter.GetSocialWarmerType) { (rsult:ResponseBaseArray<[SocialWarmer]>) in
print(rsult.data)
}
如果您的 Api 返回单个对象而不是使用 ResponseBaseObject。
BaseApiClient.default.fetch(request: APIRouter.GetSocialWarmerType) { (rsult:ResponseBaseObject<SocialWarmer>) in
print(rsult.data)
}
但是只有当您已经知道您的 Api 将返回单个对象或对象数组时,此解决方案仍然有效。
推荐阅读
- javascript - 如何在“添加新”事件后仅将更改应用于新字段?
- kubernetes - 查询 Kubernetes 指标-服务器指标值
- playframework - Playframework:`reference.conf` 未在 Kubernetes (Openshift) 中加载
- python - 带有图像的 Tkinter 画布只是白色的
- php - 我的代码没有从 mysql 数据库中读取
- java - 如何在java中创建下面给出的json?
- tensorflow - TF 估计梯度提升分类器在训练时突然停止
- sql - 获取最后一个值和今年的值进行比较
- android - 不断收到解析错误启动文件提供程序意图
- sql - 如何将现有主键替换为另一个现有主键以合并两条不同的记录?