I am very new to Swift and I am trying to create a tree of classes to model my data. I want to use JSONEncoder and JSONDecoder to send and receive objects. I have an issue when decoding generic classes inside another object (nested) because in the init(from: decoder) method I do not have access to other properties that could help me.
In my code:
NestedSecondObject extends NestedObjects, which extends Codable - NestedObject can be extended by NesteThirtObject and so on...
Contact extends Object1, which extends Codable; Contact contains a NestedObject type (which can be any subclass of NestedObject at runtime)
Because JSONEncoder and JSONDecoder do not support inheritance by default, i override the methods "encode" and init(from: decoder) as described here: Using Decodable in Swift 4 with Inheritance
My code is:
class NestedSecondObject: NestedObject {
var subfield2: Int?
private enum CodingKeys : String, CodingKey {
case subfield2
}
override init() { super.init() }
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(subfield2, forKey: .subfield2)
}
required init(from decoder: Decoder) throws
{
try super.init(from: decoder)
let values = try decoder.container(keyedBy: CodingKeys.self)
self.subfield2 = try values.decode(Int.self, forKey: .subfield2)
}
}
class Contact:Object1 {
var name: String = ""
var age: Int = 0
var address: String = ""
// var className = "biz.ebas.platform.generic.shared.EContactModel"
var nestedObject:NestedObject?
private enum CodingKeys : String, CodingKey {
case name,age,address,nestedObject
}
override init() { super.init() }
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(age, forKey: .age)
try container.encode(address, forKey: .address)
try container.encode(nestedObject, forKey: .nestedObject)
}
required init(from decoder: Decoder) throws
{
try super.init(from: decoder)
print(type(of: self))
print(type(of: decoder))
let values = try decoder.container(keyedBy: CodingKeys.self)
print(type(of: values))
self.name = try values.decode(String.self, forKey: .name)
self.age = try values.decode(Int.self, forKey: .age)
self.address = try values.decode(String.self, forKey: .address)
self.nestedObject = try values.decodeIfPresent(???.self, forKey: .nestedObject) // HERE i need to know what ???.self is
}
}
Decoding is:
let jsonDec = JSONDecoder()
let jsonData = json?.data(using: .utf8)
let decodedContact: Contact = try jsonDec.decode(Contact.self, from: jsonData!)
So, basically, when I make a request to the server, I know what types I receive (let's say I request NestedSecondObject), but how do I pass it to the method "init(from decoder:Decoder)" ?
I tried to extend class JSONDecoder and add a simple property like this:
class EJSONDecoder: JSONDecoder {
var classType:AnyObject.Type?
}
But inside the required init(from decoder:Decoder) method, the type of decoder is not EJSONDecoder, is _JSONDecoder, so I cannot access the decoder.classType property.
Can anyone help with a solution or some sort of workaround? Thanks!