Reputation: 14113
JSON :
{
"rows" :
[
{
"_id": "5cdc0ede5c3dcb04bdb3a972",
"emp_code": 187,
"log_id": 361711,
"punch_time": "2019-05-07T04:00:33.000Z",
"pin_type": 1,
"status": 4,
"__v": 0
},
{
"_id": "5cdc40de5c3dcb04bdb3a972",
"emp_code": 111,
"log_id": 361701,
"punch_time": "2019-05-07T04:00:35.000Z",
"pin_type": 101,
"status": 4,
"__v": 0
}
],
"pin_type_text": {
"1": "In Fingerprint",
"4": "In Card",
"101": "Out Fingerprint",
"104": "Out Card"
}
}
The value of pin_type in each row refers to the record in pin_type_text mapped with it's key.
I am using AlamofireObjectMapper for creating models, and here is the PinTypeText model :
class PinTypeText : Mappable {
var inFingerprint: String?
var inCard: String?
var outFingerprint: String?
var outCard: String?
required init?(map: Map) {
}
func mapping(map: Map) {
self.inFingerprint <- map["1"]
self.inCard <- map["4"]
self.outFingerprint <- map["101"]
self.outCard <- map["104"]
}
}
Issue : Suppose in future, the pin_type values - 1, 4, 101, 104 change in the backend, how can I handle such a case without changing my model. As per this model structure, I need to change my model class every time the backend model changes
Upvotes: 1
Views: 100
Reputation: 24341
Here is how you can use Codable
as a solution,
1. Create a model Row
that will contain the data of a single row in rows
array of json
, i.e.
class Row: Decodable {
var id: String?
var pinType: String?
var pinId: Int?
enum CodingKeys: String, CodingKey {
case id = "_id"
case pinId = "pin_type"
}
}
In the above model, I've used 2 different properties - pinType and pinId
.
pinId
will contain the pin_type
value in the row
pinType
will contain the actual value corresponding to pinId
. We'll fill this value later.
Also, I've only used a small set of keys of the row
. You can add more as required.
2. Next create another model Response
that will contain an array
of Row
, i.e.
class Response: Decodable {
var rows: [Row]?
enum CodingKeys: String, CodingKey {
case rows, pin_type_text
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
rows = try values.decodeIfPresent([Row].self, forKey: .rows)
let pinTypeText = try values.decodeIfPresent([String:String].self, forKey: .pin_type_text)
rows?.forEach({ (row) in
if let pinId = row.pinId {
row.pinType = pinTypeText?[String(pinId)]
}
})
}
}
In the above model,
rows
array in json
is parsed as [Row]
.
pinTypeText
dictionary
is parsed as [String:String]
type.
[Row]
is enumerated to fill pinType
in each row
using pinId
and pinTypeText dictionary
.
When using, you need to use pinType
property of a Row
object.
response?.rows?.forEach({ print($0.pinType) }) //This line will print - "In Fingerprint" and "Out Fingerprint"
Let me know in case you face issue implementing this approach.
Upvotes: 2