I need to integrate some server API calling in which I'm likely to get response as:
+ Response 200 (application/json)
+ Body
{
"code": 0,
"status": "ok",
"message": "Verification Message from API",
"result": {
"values": [
{
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
}
]
}
}
Sometimes the response will be as:
+ Response 200 (application/json)
+ Body
{
"code": 0,
"status": "ok",
"message": "Update Message from API",
"result": {
"values": [
{
"update_status": true
}
]
}
}
As you can see, mostly all the fields of the response body are pretty much same. The only distinction is going to be in the section:
"values": [
{
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
}
]
or,
"values": [
{
"update_status": true
}
]
I have followed the Protocol Oriented Programming (POP) pattern for my use case.
Solution:
struct Value {
let verifyStatus: Bool?
let updateStatus: Bool?
let tokenKey: String?
}
extension Value: Codable {
private enum ValueCodingKeys: String, CodingKey {
case verifyStatus = "verify_status"
case updateStatus = "update_status"
case tokenKey = "token_key"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ValueCodingKeys.self)
try container.encode(verifyStatus, forKey: .verifyStatus)
try container.encode(updateStatus, forKey: .updateStatus)
try container.encode(tokenKey, forKey: .tokenKey)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ValueCodingKeys.self)
verifyStatus = try container.decodeIfPresent(Bool.self, forKey: .verifyStatus)
updateStatus = try container.decodeIfPresent(Bool.self, forKey: .updateStatus)
tokenKey = try container.decodeIfPresent(String.self, forKey: .tokenKey)
}
}
protocol Resultable {
var values: [Value] { get }
}
struct Result: Resultable {
let values: [Value]
}
extension Result: Codable {
private enum ResultCodingKeys: String, CodingKey {
case values
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ResultCodingKeys.self)
try container.encode(values, forKey: .values)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ResultCodingKeys.self)
values = try container.decode([Value].self, forKey: .values)
}
}
protocol Responsable {
var code: Int { get }
var status: String { get }
var message: String { get }
var result: Result { get }
}
struct Response: Responsable {
let code: Int
let status: String
let message: String
let result: Result
}
extension Response: Codable {
private enum ResponeCodingKeys: String, CodingKey {
case code
case status
case message
case result
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ResponeCodingKeys.self)
try container.encode(code, forKey: .code)
try container.encode(status, forKey: .status)
try container.encode(message, forKey: .message)
try container.encode(result, forKey: .result)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ResponeCodingKeys.self)
code = try container.decode(Int.self, forKey: .code)
status = try container.decode(String.self, forKey: .status)
message = try container.decode(String.self, forKey: .message)
result = try container.decode(Result.self, forKey: .result)
}
}
I think I'm over-complicating the solution. Is there any better solution to achieve this?
Do I really have to have two
protocol
defined?