Reputation: 59
I have been using codable in Swift from a long time and it has almost solved all the issues I had until I encountered the below mentioned issue. Suppose, the data coming from the API is something like this:
{
"name": "ABC",
"contactNo": "123",
"gender": "M",
"dob": "02-01-1998",
"qualitifcation": "XYZ"
}
Now, I want to parse some part of this data in One model and some part in another model and have the reference of another model in the first model. For Eg:
struct ModelOne: Codable {
let name: String?
let contactNo: String?
let modelTwoRef: ModelTwo?
}
struct ModelTwo: Codable {
let gender: String?
let dob: String?
let qualification: String?
}
I am not able to solve this problem. It would be really helpful is someone can help with the same.
Upvotes: 2
Views: 663
Reputation: 33
struct ModelOne: Codable {
var name: String
var contactNo: String
var gender: String
var dob: Date
var qualification: String
}
let json = """
{
"name": "ABC",
"contactNo": "123",
"gender": "M",
"dob": "02.01.1998",
"qualification": "XYZ"
}
""".data(using: .utf8)!
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yy"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
let response = try decoder.decode(ModelOne.self, from: json)
print("Name: \(response.name)")
print("Contact no: \(response.contactNo)")
print("Gender: \(response.gender)")
print("Date: \(response.dob)")
print("qualification \(response.qualification)")
Upvotes: 0
Reputation: 26006
You can solve this by overriding init(from:)
:
struct ModelOne: Codable {
let name: String?
let contactNo: String?
let modelTwoRef: ModelTwo?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.contactNo = try container.decode(String.self, forKey: .contactNo)
self.modelTwoRef = try ModelTwo(from: decoder)
}
}
struct ModelTwo: Codable {
let gender: String?
let dob: String?
let qualification: String?
}
Test sample:
let jsonStr = """
{
"name": "ABC",
"contactNo": "123",
"gender": "M",
"dob": "02-01-1998",
"qualification": "XYZ"
}
"""
let jsonData = Data(jsonStr.utf8)
do {
let model = try JSONDecoder().decode(ModelOne.self, from: jsonData)
print(model)
} catch {
print("Error: \(error)")
}
Small diff from your question which I guess was a typo: "qualification“ vs "qualitifcation".
Upvotes: 2
Reputation: 630
You need to implement decoding by hand in ModelOne
and pass on the decoder. You can do this by implementing the init
method of ModelOne
like this:
struct ModelOne: Codable {
let name: String?
let contactNo: String?
let modelTwoRef: ModelTwo?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: .name)
contactNo = try container.decodeIfPresent(String.self, forKey: .contactNo)
modelTwoRef = try ModelTwo(from: decoder)
}
enum CodingKeys: String, CodingKey {
case name, contactNo
}
}
struct ModelTwo: Codable {
let gender: String?
let dob: String?
let qualification: String?
}
I hope it solves your problem. /Martin
Upvotes: 0
Reputation: 11
Create JSON decode function like below function
func decode<T: Decodable>(from data: Data, to decodableType: T.Type) -> T? {
do {
let decodedData = try decoder.decode(decodableType, from: data)
return decodedData
} catch let error {
print(error)
}
return nil
}
check API response data nil or not using guard statement
guard let decoadedData = self.jsonHelper.decode(from: data, to: ModelOne.self) else {
return
}
Upvotes: 1