Using Decodable with inheritance raises an exception
There is no need to use the superDecoder
, you can simply do this (I changed the variable names to lowercase to conform to the naming convention)
class LoginResponse: BaseResponse {
let message: String
private enum CodingKeys: String, CodingKey{
case message = "Message"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
message = try container.decode(String.self, forKey: .message)
try super.init(from: decoder)
}
}
class BaseResponse: Decodable {
let status: Int
private enum CodingKeys: String, CodingKey{
case status = "Status"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(Int.self, forKey: .status)
}
}
decoder.decode(BaseResponse.self ...
decodes onlystatus
decoder.decode(LoginResponse.self ...
decodesstatus
andmessage
And never en-/decode with try!
. Handle the error.
how to use inheritance in decodable model
Rather than inheritance and classes use generics and structs because Decodable
doesn't support inheritance by default.
For example create a struct JSONParser
struct JSONParser<T : Decodable> {
struct ResponseData<U : Decodable> : Decodable {
let total, count : Int
let results : [U]
}
let code : Int
let status : String
let data : ResponseData<T>
init(data: Data) throws {
let decoder = JSONDecoder()
data = try decoder.decode(ResponseData.self, from: data)
}
}
And use it for the dictionary containing id
and title
struct Item {
let id : Int
let title : String
}
do {
let jsonParser = try JSONParser<Item>(data: data)
let results = jsonParser.data.results
} catch { print(error) }
Inheritance of Encodable Class
Encodable
and Decodable
involve some code synthesis where the compiler essentially writes the code for you. When you conform BasicData
to Encodable
, these methods are written to the BasicData
class and hence they are not aware of any additional properties defined by subclasses. You have to override the encode(to:)
method in your subclass:
class AdditionalData: BasicData {
let c: String
let d: Int
init(c: String, d: Int) {
self.c = c
self.d = d
}
private enum CodingKeys: String, CodingKey {
case c, d
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.c, forKey: .c)
try container.encode(self.d, forKey: .d)
}
}
See this question for a similar problem with Decodable
.
Swift Attempt to a decode a Codable parent class as one of its subclasses
You need to have a custom init(from:)
in Main
and decode a
to the right subclass directly
class Main: Codable {
let a: A
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let object = try? container.decode(B1.self, forKey: .a) {
a = object
} else {
a = try container.decode(B2.self, forKey: .a)
}
}
}
Codable with inheritance
If anyone is having the same problem I found below solution.
class DataManager: Codable {
var bases: [Base] = []
init(bases: [Base]) {
self.bases = bases
}
private enum CodingKeys: String, CodingKey {
case bases
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var list = try container.nestedUnkeyedContainer(forKey: DataManager.CodingKeys.bases)
while !list.isAtEnd {
if let child1 = try? list.decode(Child1.self) {
bases.append(child1 as Base)
} else if let child2 = try? list.decode(Child2.self) {
bases.append(child2 as Base)
}
}
}
}
Is Decodable inheritance even possible in Swift?
This worked:
class Party: Decodable {
var theme: String
}
class PublicParty : Party {
var address: String = ""
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let values = try decoder.container(keyedBy: CodingKeys.self)
address = try values.decode(String.self, forKey: .address)
}
private enum CodingKeys: String, CodingKey
{
case address
}
}
Codable for class with Inheritance
You should use decodeIfPresent
for your optional properties
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
try super.init(from: decoder)
}
Also note the change to super that was needed to avoid another error. You can also change your catch slightly from
} catch let error as NSError {
print(error)
}
to simply
} catch {
print(error)
}
Related Topics
Any Way to Replace Characters on Swift String
How to Convert a Date String With Optional Fractional Seconds Using Codable in Swift
How to Generate a Random Number in Swift Without Repeating the Previous Random Number
All Dates Between Two Date Objects (Swift)
Get Class Name of Object as String in Swift
How to Make a Random Color With Swift
How to Get Monday'S Date of the Current Week in Swift
How to Load Url in Uiwebview in Swift
Firebase Getting Data in Order
Shall We Always Use [Unowned Self] Inside Closure in Swift
Waiting Until the Task Finishes
Deinit Method Is Never Called - Swift Playground
What Does It Mean That String and Character Comparisons in Swift Are Not Locale-Sensitive
How to Round a Double to the Nearest Int in Swift
How to Compare Two Dictionaries in Swift