Using Decodable in Swift 4 with Inheritance












43















Should the use of class inheritance break the Decodability of class. For example, the following code



class Server : Codable {
var id : Int?
}

class Development : Server {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here


output is:



1
name is nil


Now if I reverse this, name decodes but id does not.



class Server {
var id : Int?
}

class Development : Server, Codable {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil")


output is:



id is nil
Large Building Development


And you can't express Codable in both classes.










share|improve this question




















  • 1





    Interesting. Have you filed a bug with Apple?

    – Code Different
    Jun 14 '17 at 23:52











  • It's not a bug, it's literally an "undocumented feature". :-) The only reference to (half of) the solution was in the 2017 WWDC "What's New In Foundation" video, detailed in my answer below.

    – Joshua Nozzi
    Jun 17 '17 at 14:29
















43















Should the use of class inheritance break the Decodability of class. For example, the following code



class Server : Codable {
var id : Int?
}

class Development : Server {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here


output is:



1
name is nil


Now if I reverse this, name decodes but id does not.



class Server {
var id : Int?
}

class Development : Server, Codable {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil")


output is:



id is nil
Large Building Development


And you can't express Codable in both classes.










share|improve this question




















  • 1





    Interesting. Have you filed a bug with Apple?

    – Code Different
    Jun 14 '17 at 23:52











  • It's not a bug, it's literally an "undocumented feature". :-) The only reference to (half of) the solution was in the 2017 WWDC "What's New In Foundation" video, detailed in my answer below.

    – Joshua Nozzi
    Jun 17 '17 at 14:29














43












43








43


18






Should the use of class inheritance break the Decodability of class. For example, the following code



class Server : Codable {
var id : Int?
}

class Development : Server {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here


output is:



1
name is nil


Now if I reverse this, name decodes but id does not.



class Server {
var id : Int?
}

class Development : Server, Codable {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil")


output is:



id is nil
Large Building Development


And you can't express Codable in both classes.










share|improve this question
















Should the use of class inheritance break the Decodability of class. For example, the following code



class Server : Codable {
var id : Int?
}

class Development : Server {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here


output is:



1
name is nil


Now if I reverse this, name decodes but id does not.



class Server {
var id : Int?
}

class Development : Server, Codable {
var name : String?
var userId : Int?
}

var json = "{"id" : 1,"name" : "Large Building Development"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development

print(item.id ?? "id is nil")
print(item.name ?? "name is nil")


output is:



id is nil
Large Building Development


And you can't express Codable in both classes.







swift swift4 codable






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Oct 18 '17 at 18:03









Guilherme

5,21764385




5,21764385










asked Jun 14 '17 at 20:13









Kevin McQuownKevin McQuown

216136




216136








  • 1





    Interesting. Have you filed a bug with Apple?

    – Code Different
    Jun 14 '17 at 23:52











  • It's not a bug, it's literally an "undocumented feature". :-) The only reference to (half of) the solution was in the 2017 WWDC "What's New In Foundation" video, detailed in my answer below.

    – Joshua Nozzi
    Jun 17 '17 at 14:29














  • 1





    Interesting. Have you filed a bug with Apple?

    – Code Different
    Jun 14 '17 at 23:52











  • It's not a bug, it's literally an "undocumented feature". :-) The only reference to (half of) the solution was in the 2017 WWDC "What's New In Foundation" video, detailed in my answer below.

    – Joshua Nozzi
    Jun 17 '17 at 14:29








1




1





Interesting. Have you filed a bug with Apple?

– Code Different
Jun 14 '17 at 23:52





Interesting. Have you filed a bug with Apple?

– Code Different
Jun 14 '17 at 23:52













It's not a bug, it's literally an "undocumented feature". :-) The only reference to (half of) the solution was in the 2017 WWDC "What's New In Foundation" video, detailed in my answer below.

– Joshua Nozzi
Jun 17 '17 at 14:29





It's not a bug, it's literally an "undocumented feature". :-) The only reference to (half of) the solution was in the 2017 WWDC "What's New In Foundation" video, detailed in my answer below.

– Joshua Nozzi
Jun 17 '17 at 14:29












5 Answers
5






active

oldest

votes


















47














I believe in the case of inheritance you must implement Coding yourself. That is, you must specify CodingKeys and implement init(from:) and encode(to:) in both superclass and subclass. Per the WWDC video (around 49:28, pictured below), you must call super with the super encoder/decoder.



WWDC 2017 Session 212 Screenshot at 49:28 (Source Code)



required init(from decoder: Decoder) throws {

// Get our container for this subclass' coding keys
let container = try decoder.container(keyedBy: CodingKeys.self)
myVar = try container.decode(MyType.self, forKey: .myVar)
// otherVar = ...

// Get superDecoder for superclass and call super.init(from:) with it
let superDecoder = try container.superDecoder()
try super.init(from: superDecoder)

}


The video seems to stop short of showing the encoding side (but it's container.superEncoder() for the encode(to:) side) but it works in much the same way in your encode(to:) implementation. I can confirm this works in this simple case (see playground code below).



I'm still struggling with some odd behavior myself with a much more complex model I'm converting from NSCoding, which has lots of newly-nested types (including struct and enum) that's exhibiting this unexpected nil behavior and "shouldn't be". Just be aware there may be edge cases that involve nested types.



Edit: Nested types seem to work fine in my test playground; I now suspect something wrong with self-referencing classes (think children of tree nodes) with a collection of itself that also contains instances of that class' various subclasses. A test of a simple self-referencing class decodes fine (that is, no subclasses) so I'm now focusing my efforts on why the subclasses case fails.



Update June 25 '17: I ended up filing a bug with Apple about this. rdar://32911973 - Unfortunately an encode/decode cycle of an array of Superclass that contains Subclass: Superclass elements will result in all elements in the array being decoded as Superclass (the subclass' init(from:) is never called, resulting in data loss or worse).



//: Fully-Implemented Inheritance

class FullSuper: Codable {

var id: UUID?

init() {}

private enum CodingKeys: String, CodingKey { case id }

required init(from decoder: Decoder) throws {

let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)

}

func encode(to encoder: Encoder) throws {

var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)

}

}

class FullSub: FullSuper {

var string: String?
private enum CodingKeys: String, CodingKey { case string }

override init() { super.init() }

required init(from decoder: Decoder) throws {

let container = try decoder.container(keyedBy: CodingKeys.self)
let superdecoder = try container.superDecoder()
try super.init(from: superdecoder)

string = try container.decode(String.self, forKey: .string)

}

override func encode(to encoder: Encoder) throws {

var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(string, forKey: .string)

let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)

}
}

let fullSub = FullSub()
fullSub.id = UUID()
fullSub.string = "FullSub"

let fullEncoder = PropertyListEncoder()
let fullData = try fullEncoder.encode(fullSub)

let fullDecoder = PropertyListDecoder()
let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)


Both the super- and subclass properties are restored in fullSubDecoded.






share|improve this answer


























  • glad I can be at peace with this issue at least for now...

    – Charlton Provatas
    Aug 29 '17 at 0:50






  • 3





    was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

    – Charlton Provatas
    Aug 29 '17 at 1:03













  • Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

    – Harry Bloom
    Sep 25 '17 at 11:56






  • 1





    Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

    – Lal Krishna
    Jun 6 '18 at 9:39






  • 3





    i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

    – Lal Krishna
    Jun 7 '18 at 4:55



















5














Found This Link - Go down to inheritance section



override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(employeeID, forKey: .employeeID)
}


For Decoding I did this:



 required init(from decoder: Decoder) throws {

try super.init(from: decoder)

let values = try decoder.container(keyedBy: CodingKeys.self)
total = try values.decode(Int.self, forKey: .total)
}

private enum CodingKeys: String, CodingKey
{
case total

}





share|improve this answer
























  • Nice blog post! Thank you for sharing.

    – Tom Calmon
    Mar 19 '18 at 23:53











  • This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

    – Tamás Sengel
    Oct 4 '18 at 0:35



















4














I was able to make it work by making my base class and subclasses conform to Decodable instead of Codable. If I used Codable it would crash in odd ways, such as getting a EXC_BAD_ACCESS when accessing a field of the subclass, yet the debugger could display all the subclass values with no problem.



Additionally, passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.






share|improve this answer


























  • Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

    – Jack Song
    Dec 30 '17 at 11:24











  • faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

    – Doro
    May 28 '18 at 15:14



















4














How about using the following way?



protocol Parent: Codable {
var inheritedProp: Int? {get set}
}

struct Child: Parent {
var inheritedProp: Int?
var title: String?

enum CodingKeys: String, CodingKey {
case inheritedProp = "inherited_prop"
case title = "short_title"
}
}


Additional info on composition: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/






share|improve this answer





















  • 3





    How does this solve the problem of decoding a heterogenous array?

    – Joshua Nozzi
    Oct 19 '17 at 15:25






  • 1





    Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

    – Joshua Nozzi
    Nov 7 '17 at 16:55











  • In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

    – Tommie C.
    Nov 13 '17 at 13:30











  • I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

    – Natanel
    Jan 9 '18 at 17:23






  • 2





    This isn't composition.

    – mxcl
    May 16 '18 at 18:28



















1














Here is a library TypePreservingCodingAdapter to do just that (can be installed with Cocoapods or SwiftPackageManager).



The code below compiles and works just fine with Swift 4.2. Unfortunately for every subclass you'll need to implement encoding and decoding of properties on your own.



import TypePreservingCodingAdapter
import Foundation

// redeclared your types with initializers
class Server: Codable {
var id: Int?

init(id: Int?) {
self.id = id
}
}

class Development: Server {
var name: String?
var userId: Int?

private enum CodingKeys: String, CodingKey {
case name
case userId
}

init(id: Int?, name: String?, userId: Int?) {
self.name = name
self.userId = userId
super.init(id: id)
}

required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let container = try decoder.container(keyedBy: CodingKeys.self)

name = try container.decodeIfPresent(String.self, forKey: .name)
userId = try container.decodeIfPresent(Int.self, forKey: .userId)
}

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(userId, forKey: .userId)
}

}

// create and adapter
let adapter = TypePreservingCodingAdapter()
let encoder = JSONEncoder()
let decoder = JSONDecoder()

// inject it into encoder and decoder
encoder.userInfo[.typePreservingAdapter] = adapter
decoder.userInfo[.typePreservingAdapter] = adapter

// register your types with adapter
adapter.register(type: Server.self).register(type: Development.self)


let server = Server(id: 1)
let development = Development(id: 2, name: "dev", userId: 42)

let servers: [Server] = [server, development]

// wrap specific object with Wrap helper object
let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })

// decode object back and unwrap them force casting to a common ancestor type
let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }

// check that decoded object are of correct types
print(decodedServers.first is Server) // prints true
print(decodedServers.last is Development) // prints true





share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f44553934%2fusing-decodable-in-swift-4-with-inheritance%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    47














    I believe in the case of inheritance you must implement Coding yourself. That is, you must specify CodingKeys and implement init(from:) and encode(to:) in both superclass and subclass. Per the WWDC video (around 49:28, pictured below), you must call super with the super encoder/decoder.



    WWDC 2017 Session 212 Screenshot at 49:28 (Source Code)



    required init(from decoder: Decoder) throws {

    // Get our container for this subclass' coding keys
    let container = try decoder.container(keyedBy: CodingKeys.self)
    myVar = try container.decode(MyType.self, forKey: .myVar)
    // otherVar = ...

    // Get superDecoder for superclass and call super.init(from:) with it
    let superDecoder = try container.superDecoder()
    try super.init(from: superDecoder)

    }


    The video seems to stop short of showing the encoding side (but it's container.superEncoder() for the encode(to:) side) but it works in much the same way in your encode(to:) implementation. I can confirm this works in this simple case (see playground code below).



    I'm still struggling with some odd behavior myself with a much more complex model I'm converting from NSCoding, which has lots of newly-nested types (including struct and enum) that's exhibiting this unexpected nil behavior and "shouldn't be". Just be aware there may be edge cases that involve nested types.



    Edit: Nested types seem to work fine in my test playground; I now suspect something wrong with self-referencing classes (think children of tree nodes) with a collection of itself that also contains instances of that class' various subclasses. A test of a simple self-referencing class decodes fine (that is, no subclasses) so I'm now focusing my efforts on why the subclasses case fails.



    Update June 25 '17: I ended up filing a bug with Apple about this. rdar://32911973 - Unfortunately an encode/decode cycle of an array of Superclass that contains Subclass: Superclass elements will result in all elements in the array being decoded as Superclass (the subclass' init(from:) is never called, resulting in data loss or worse).



    //: Fully-Implemented Inheritance

    class FullSuper: Codable {

    var id: UUID?

    init() {}

    private enum CodingKeys: String, CodingKey { case id }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(UUID.self, forKey: .id)

    }

    func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)

    }

    }

    class FullSub: FullSuper {

    var string: String?
    private enum CodingKeys: String, CodingKey { case string }

    override init() { super.init() }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    let superdecoder = try container.superDecoder()
    try super.init(from: superdecoder)

    string = try container.decode(String.self, forKey: .string)

    }

    override func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(string, forKey: .string)

    let superdecoder = container.superEncoder()
    try super.encode(to: superdecoder)

    }
    }

    let fullSub = FullSub()
    fullSub.id = UUID()
    fullSub.string = "FullSub"

    let fullEncoder = PropertyListEncoder()
    let fullData = try fullEncoder.encode(fullSub)

    let fullDecoder = PropertyListDecoder()
    let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)


    Both the super- and subclass properties are restored in fullSubDecoded.






    share|improve this answer


























    • glad I can be at peace with this issue at least for now...

      – Charlton Provatas
      Aug 29 '17 at 0:50






    • 3





      was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

      – Charlton Provatas
      Aug 29 '17 at 1:03













    • Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

      – Harry Bloom
      Sep 25 '17 at 11:56






    • 1





      Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

      – Lal Krishna
      Jun 6 '18 at 9:39






    • 3





      i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

      – Lal Krishna
      Jun 7 '18 at 4:55
















    47














    I believe in the case of inheritance you must implement Coding yourself. That is, you must specify CodingKeys and implement init(from:) and encode(to:) in both superclass and subclass. Per the WWDC video (around 49:28, pictured below), you must call super with the super encoder/decoder.



    WWDC 2017 Session 212 Screenshot at 49:28 (Source Code)



    required init(from decoder: Decoder) throws {

    // Get our container for this subclass' coding keys
    let container = try decoder.container(keyedBy: CodingKeys.self)
    myVar = try container.decode(MyType.self, forKey: .myVar)
    // otherVar = ...

    // Get superDecoder for superclass and call super.init(from:) with it
    let superDecoder = try container.superDecoder()
    try super.init(from: superDecoder)

    }


    The video seems to stop short of showing the encoding side (but it's container.superEncoder() for the encode(to:) side) but it works in much the same way in your encode(to:) implementation. I can confirm this works in this simple case (see playground code below).



    I'm still struggling with some odd behavior myself with a much more complex model I'm converting from NSCoding, which has lots of newly-nested types (including struct and enum) that's exhibiting this unexpected nil behavior and "shouldn't be". Just be aware there may be edge cases that involve nested types.



    Edit: Nested types seem to work fine in my test playground; I now suspect something wrong with self-referencing classes (think children of tree nodes) with a collection of itself that also contains instances of that class' various subclasses. A test of a simple self-referencing class decodes fine (that is, no subclasses) so I'm now focusing my efforts on why the subclasses case fails.



    Update June 25 '17: I ended up filing a bug with Apple about this. rdar://32911973 - Unfortunately an encode/decode cycle of an array of Superclass that contains Subclass: Superclass elements will result in all elements in the array being decoded as Superclass (the subclass' init(from:) is never called, resulting in data loss or worse).



    //: Fully-Implemented Inheritance

    class FullSuper: Codable {

    var id: UUID?

    init() {}

    private enum CodingKeys: String, CodingKey { case id }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(UUID.self, forKey: .id)

    }

    func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)

    }

    }

    class FullSub: FullSuper {

    var string: String?
    private enum CodingKeys: String, CodingKey { case string }

    override init() { super.init() }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    let superdecoder = try container.superDecoder()
    try super.init(from: superdecoder)

    string = try container.decode(String.self, forKey: .string)

    }

    override func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(string, forKey: .string)

    let superdecoder = container.superEncoder()
    try super.encode(to: superdecoder)

    }
    }

    let fullSub = FullSub()
    fullSub.id = UUID()
    fullSub.string = "FullSub"

    let fullEncoder = PropertyListEncoder()
    let fullData = try fullEncoder.encode(fullSub)

    let fullDecoder = PropertyListDecoder()
    let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)


    Both the super- and subclass properties are restored in fullSubDecoded.






    share|improve this answer


























    • glad I can be at peace with this issue at least for now...

      – Charlton Provatas
      Aug 29 '17 at 0:50






    • 3





      was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

      – Charlton Provatas
      Aug 29 '17 at 1:03













    • Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

      – Harry Bloom
      Sep 25 '17 at 11:56






    • 1





      Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

      – Lal Krishna
      Jun 6 '18 at 9:39






    • 3





      i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

      – Lal Krishna
      Jun 7 '18 at 4:55














    47












    47








    47







    I believe in the case of inheritance you must implement Coding yourself. That is, you must specify CodingKeys and implement init(from:) and encode(to:) in both superclass and subclass. Per the WWDC video (around 49:28, pictured below), you must call super with the super encoder/decoder.



    WWDC 2017 Session 212 Screenshot at 49:28 (Source Code)



    required init(from decoder: Decoder) throws {

    // Get our container for this subclass' coding keys
    let container = try decoder.container(keyedBy: CodingKeys.self)
    myVar = try container.decode(MyType.self, forKey: .myVar)
    // otherVar = ...

    // Get superDecoder for superclass and call super.init(from:) with it
    let superDecoder = try container.superDecoder()
    try super.init(from: superDecoder)

    }


    The video seems to stop short of showing the encoding side (but it's container.superEncoder() for the encode(to:) side) but it works in much the same way in your encode(to:) implementation. I can confirm this works in this simple case (see playground code below).



    I'm still struggling with some odd behavior myself with a much more complex model I'm converting from NSCoding, which has lots of newly-nested types (including struct and enum) that's exhibiting this unexpected nil behavior and "shouldn't be". Just be aware there may be edge cases that involve nested types.



    Edit: Nested types seem to work fine in my test playground; I now suspect something wrong with self-referencing classes (think children of tree nodes) with a collection of itself that also contains instances of that class' various subclasses. A test of a simple self-referencing class decodes fine (that is, no subclasses) so I'm now focusing my efforts on why the subclasses case fails.



    Update June 25 '17: I ended up filing a bug with Apple about this. rdar://32911973 - Unfortunately an encode/decode cycle of an array of Superclass that contains Subclass: Superclass elements will result in all elements in the array being decoded as Superclass (the subclass' init(from:) is never called, resulting in data loss or worse).



    //: Fully-Implemented Inheritance

    class FullSuper: Codable {

    var id: UUID?

    init() {}

    private enum CodingKeys: String, CodingKey { case id }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(UUID.self, forKey: .id)

    }

    func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)

    }

    }

    class FullSub: FullSuper {

    var string: String?
    private enum CodingKeys: String, CodingKey { case string }

    override init() { super.init() }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    let superdecoder = try container.superDecoder()
    try super.init(from: superdecoder)

    string = try container.decode(String.self, forKey: .string)

    }

    override func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(string, forKey: .string)

    let superdecoder = container.superEncoder()
    try super.encode(to: superdecoder)

    }
    }

    let fullSub = FullSub()
    fullSub.id = UUID()
    fullSub.string = "FullSub"

    let fullEncoder = PropertyListEncoder()
    let fullData = try fullEncoder.encode(fullSub)

    let fullDecoder = PropertyListDecoder()
    let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)


    Both the super- and subclass properties are restored in fullSubDecoded.






    share|improve this answer















    I believe in the case of inheritance you must implement Coding yourself. That is, you must specify CodingKeys and implement init(from:) and encode(to:) in both superclass and subclass. Per the WWDC video (around 49:28, pictured below), you must call super with the super encoder/decoder.



    WWDC 2017 Session 212 Screenshot at 49:28 (Source Code)



    required init(from decoder: Decoder) throws {

    // Get our container for this subclass' coding keys
    let container = try decoder.container(keyedBy: CodingKeys.self)
    myVar = try container.decode(MyType.self, forKey: .myVar)
    // otherVar = ...

    // Get superDecoder for superclass and call super.init(from:) with it
    let superDecoder = try container.superDecoder()
    try super.init(from: superDecoder)

    }


    The video seems to stop short of showing the encoding side (but it's container.superEncoder() for the encode(to:) side) but it works in much the same way in your encode(to:) implementation. I can confirm this works in this simple case (see playground code below).



    I'm still struggling with some odd behavior myself with a much more complex model I'm converting from NSCoding, which has lots of newly-nested types (including struct and enum) that's exhibiting this unexpected nil behavior and "shouldn't be". Just be aware there may be edge cases that involve nested types.



    Edit: Nested types seem to work fine in my test playground; I now suspect something wrong with self-referencing classes (think children of tree nodes) with a collection of itself that also contains instances of that class' various subclasses. A test of a simple self-referencing class decodes fine (that is, no subclasses) so I'm now focusing my efforts on why the subclasses case fails.



    Update June 25 '17: I ended up filing a bug with Apple about this. rdar://32911973 - Unfortunately an encode/decode cycle of an array of Superclass that contains Subclass: Superclass elements will result in all elements in the array being decoded as Superclass (the subclass' init(from:) is never called, resulting in data loss or worse).



    //: Fully-Implemented Inheritance

    class FullSuper: Codable {

    var id: UUID?

    init() {}

    private enum CodingKeys: String, CodingKey { case id }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(UUID.self, forKey: .id)

    }

    func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)

    }

    }

    class FullSub: FullSuper {

    var string: String?
    private enum CodingKeys: String, CodingKey { case string }

    override init() { super.init() }

    required init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)
    let superdecoder = try container.superDecoder()
    try super.init(from: superdecoder)

    string = try container.decode(String.self, forKey: .string)

    }

    override func encode(to encoder: Encoder) throws {

    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(string, forKey: .string)

    let superdecoder = container.superEncoder()
    try super.encode(to: superdecoder)

    }
    }

    let fullSub = FullSub()
    fullSub.id = UUID()
    fullSub.string = "FullSub"

    let fullEncoder = PropertyListEncoder()
    let fullData = try fullEncoder.encode(fullSub)

    let fullDecoder = PropertyListDecoder()
    let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)


    Both the super- and subclass properties are restored in fullSubDecoded.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jun 7 '18 at 11:33

























    answered Jun 17 '17 at 14:21









    Joshua NozziJoshua Nozzi

    56.4k12125126




    56.4k12125126













    • glad I can be at peace with this issue at least for now...

      – Charlton Provatas
      Aug 29 '17 at 0:50






    • 3





      was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

      – Charlton Provatas
      Aug 29 '17 at 1:03













    • Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

      – Harry Bloom
      Sep 25 '17 at 11:56






    • 1





      Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

      – Lal Krishna
      Jun 6 '18 at 9:39






    • 3





      i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

      – Lal Krishna
      Jun 7 '18 at 4:55



















    • glad I can be at peace with this issue at least for now...

      – Charlton Provatas
      Aug 29 '17 at 0:50






    • 3





      was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

      – Charlton Provatas
      Aug 29 '17 at 1:03













    • Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

      – Harry Bloom
      Sep 25 '17 at 11:56






    • 1





      Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

      – Lal Krishna
      Jun 6 '18 at 9:39






    • 3





      i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

      – Lal Krishna
      Jun 7 '18 at 4:55

















    glad I can be at peace with this issue at least for now...

    – Charlton Provatas
    Aug 29 '17 at 0:50





    glad I can be at peace with this issue at least for now...

    – Charlton Provatas
    Aug 29 '17 at 0:50




    3




    3





    was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

    – Charlton Provatas
    Aug 29 '17 at 1:03







    was able to work around the issue for now by converting the base class to a protocol and add default implementations to the protocol extension and have the derived class conform to it

    – Charlton Provatas
    Aug 29 '17 at 1:03















    Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

    – Harry Bloom
    Sep 25 '17 at 11:56





    Same as Charlton. Was running into EXC_BAD_ACCESS errors when decoding with a base class. Had to move over to a protocol structure to get around it.

    – Harry Bloom
    Sep 25 '17 at 11:56




    1




    1





    Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

    – Lal Krishna
    Jun 6 '18 at 9:39





    Actually container.superDecoder() don't needed. super.init(from: decoder) is enough

    – Lal Krishna
    Jun 6 '18 at 9:39




    3




    3





    i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

    – Lal Krishna
    Jun 7 '18 at 4:55





    i run the code swift 4.1. And I got exception while using superDecoder. And working fine with super.init(from: decoder)

    – Lal Krishna
    Jun 7 '18 at 4:55













    5














    Found This Link - Go down to inheritance section



    override func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(employeeID, forKey: .employeeID)
    }


    For Decoding I did this:



     required init(from decoder: Decoder) throws {

    try super.init(from: decoder)

    let values = try decoder.container(keyedBy: CodingKeys.self)
    total = try values.decode(Int.self, forKey: .total)
    }

    private enum CodingKeys: String, CodingKey
    {
    case total

    }





    share|improve this answer
























    • Nice blog post! Thank you for sharing.

      – Tom Calmon
      Mar 19 '18 at 23:53











    • This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

      – Tamás Sengel
      Oct 4 '18 at 0:35
















    5














    Found This Link - Go down to inheritance section



    override func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(employeeID, forKey: .employeeID)
    }


    For Decoding I did this:



     required init(from decoder: Decoder) throws {

    try super.init(from: decoder)

    let values = try decoder.container(keyedBy: CodingKeys.self)
    total = try values.decode(Int.self, forKey: .total)
    }

    private enum CodingKeys: String, CodingKey
    {
    case total

    }





    share|improve this answer
























    • Nice blog post! Thank you for sharing.

      – Tom Calmon
      Mar 19 '18 at 23:53











    • This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

      – Tamás Sengel
      Oct 4 '18 at 0:35














    5












    5








    5







    Found This Link - Go down to inheritance section



    override func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(employeeID, forKey: .employeeID)
    }


    For Decoding I did this:



     required init(from decoder: Decoder) throws {

    try super.init(from: decoder)

    let values = try decoder.container(keyedBy: CodingKeys.self)
    total = try values.decode(Int.self, forKey: .total)
    }

    private enum CodingKeys: String, CodingKey
    {
    case total

    }





    share|improve this answer













    Found This Link - Go down to inheritance section



    override func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(employeeID, forKey: .employeeID)
    }


    For Decoding I did this:



     required init(from decoder: Decoder) throws {

    try super.init(from: decoder)

    let values = try decoder.container(keyedBy: CodingKeys.self)
    total = try values.decode(Int.self, forKey: .total)
    }

    private enum CodingKeys: String, CodingKey
    {
    case total

    }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Jan 30 '18 at 13:52









    devjmedevjme

    14416




    14416













    • Nice blog post! Thank you for sharing.

      – Tom Calmon
      Mar 19 '18 at 23:53











    • This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

      – Tamás Sengel
      Oct 4 '18 at 0:35



















    • Nice blog post! Thank you for sharing.

      – Tom Calmon
      Mar 19 '18 at 23:53











    • This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

      – Tamás Sengel
      Oct 4 '18 at 0:35

















    Nice blog post! Thank you for sharing.

    – Tom Calmon
    Mar 19 '18 at 23:53





    Nice blog post! Thank you for sharing.

    – Tom Calmon
    Mar 19 '18 at 23:53













    This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

    – Tamás Sengel
    Oct 4 '18 at 0:35





    This answer actually works better than the accepted one if you want to save a variable with a Codable subclass type to UserDefaults.

    – Tamás Sengel
    Oct 4 '18 at 0:35











    4














    I was able to make it work by making my base class and subclasses conform to Decodable instead of Codable. If I used Codable it would crash in odd ways, such as getting a EXC_BAD_ACCESS when accessing a field of the subclass, yet the debugger could display all the subclass values with no problem.



    Additionally, passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.






    share|improve this answer


























    • Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

      – Jack Song
      Dec 30 '17 at 11:24











    • faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

      – Doro
      May 28 '18 at 15:14
















    4














    I was able to make it work by making my base class and subclasses conform to Decodable instead of Codable. If I used Codable it would crash in odd ways, such as getting a EXC_BAD_ACCESS when accessing a field of the subclass, yet the debugger could display all the subclass values with no problem.



    Additionally, passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.






    share|improve this answer


























    • Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

      – Jack Song
      Dec 30 '17 at 11:24











    • faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

      – Doro
      May 28 '18 at 15:14














    4












    4








    4







    I was able to make it work by making my base class and subclasses conform to Decodable instead of Codable. If I used Codable it would crash in odd ways, such as getting a EXC_BAD_ACCESS when accessing a field of the subclass, yet the debugger could display all the subclass values with no problem.



    Additionally, passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.






    share|improve this answer















    I was able to make it work by making my base class and subclasses conform to Decodable instead of Codable. If I used Codable it would crash in odd ways, such as getting a EXC_BAD_ACCESS when accessing a field of the subclass, yet the debugger could display all the subclass values with no problem.



    Additionally, passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Mar 4 '18 at 13:33









    Milad Faridnia

    5,580104661




    5,580104661










    answered Oct 5 '17 at 21:07









    ShackBurgerShackBurger

    512




    512













    • Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

      – Jack Song
      Dec 30 '17 at 11:24











    • faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

      – Doro
      May 28 '18 at 15:14



















    • Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

      – Jack Song
      Dec 30 '17 at 11:24











    • faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

      – Doro
      May 28 '18 at 15:14

















    Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

    – Jack Song
    Dec 30 '17 at 11:24





    Same trick: passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.

    – Jack Song
    Dec 30 '17 at 11:24













    faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

    – Doro
    May 28 '18 at 15:14





    faced same issue. is there any way to solve this without fully implementing encode/ decode methods? thanks

    – Doro
    May 28 '18 at 15:14











    4














    How about using the following way?



    protocol Parent: Codable {
    var inheritedProp: Int? {get set}
    }

    struct Child: Parent {
    var inheritedProp: Int?
    var title: String?

    enum CodingKeys: String, CodingKey {
    case inheritedProp = "inherited_prop"
    case title = "short_title"
    }
    }


    Additional info on composition: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/






    share|improve this answer





















    • 3





      How does this solve the problem of decoding a heterogenous array?

      – Joshua Nozzi
      Oct 19 '17 at 15:25






    • 1





      Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

      – Joshua Nozzi
      Nov 7 '17 at 16:55











    • In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

      – Tommie C.
      Nov 13 '17 at 13:30











    • I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

      – Natanel
      Jan 9 '18 at 17:23






    • 2





      This isn't composition.

      – mxcl
      May 16 '18 at 18:28
















    4














    How about using the following way?



    protocol Parent: Codable {
    var inheritedProp: Int? {get set}
    }

    struct Child: Parent {
    var inheritedProp: Int?
    var title: String?

    enum CodingKeys: String, CodingKey {
    case inheritedProp = "inherited_prop"
    case title = "short_title"
    }
    }


    Additional info on composition: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/






    share|improve this answer





















    • 3





      How does this solve the problem of decoding a heterogenous array?

      – Joshua Nozzi
      Oct 19 '17 at 15:25






    • 1





      Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

      – Joshua Nozzi
      Nov 7 '17 at 16:55











    • In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

      – Tommie C.
      Nov 13 '17 at 13:30











    • I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

      – Natanel
      Jan 9 '18 at 17:23






    • 2





      This isn't composition.

      – mxcl
      May 16 '18 at 18:28














    4












    4








    4







    How about using the following way?



    protocol Parent: Codable {
    var inheritedProp: Int? {get set}
    }

    struct Child: Parent {
    var inheritedProp: Int?
    var title: String?

    enum CodingKeys: String, CodingKey {
    case inheritedProp = "inherited_prop"
    case title = "short_title"
    }
    }


    Additional info on composition: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/






    share|improve this answer















    How about using the following way?



    protocol Parent: Codable {
    var inheritedProp: Int? {get set}
    }

    struct Child: Parent {
    var inheritedProp: Int?
    var title: String?

    enum CodingKeys: String, CodingKey {
    case inheritedProp = "inherited_prop"
    case title = "short_title"
    }
    }


    Additional info on composition: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jun 8 '18 at 8:39

























    answered Oct 14 '17 at 16:49









    NavNav

    4,433134474




    4,433134474








    • 3





      How does this solve the problem of decoding a heterogenous array?

      – Joshua Nozzi
      Oct 19 '17 at 15:25






    • 1





      Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

      – Joshua Nozzi
      Nov 7 '17 at 16:55











    • In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

      – Tommie C.
      Nov 13 '17 at 13:30











    • I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

      – Natanel
      Jan 9 '18 at 17:23






    • 2





      This isn't composition.

      – mxcl
      May 16 '18 at 18:28














    • 3





      How does this solve the problem of decoding a heterogenous array?

      – Joshua Nozzi
      Oct 19 '17 at 15:25






    • 1





      Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

      – Joshua Nozzi
      Nov 7 '17 at 16:55











    • In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

      – Tommie C.
      Nov 13 '17 at 13:30











    • I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

      – Natanel
      Jan 9 '18 at 17:23






    • 2





      This isn't composition.

      – mxcl
      May 16 '18 at 18:28








    3




    3





    How does this solve the problem of decoding a heterogenous array?

    – Joshua Nozzi
    Oct 19 '17 at 15:25





    How does this solve the problem of decoding a heterogenous array?

    – Joshua Nozzi
    Oct 19 '17 at 15:25




    1




    1





    Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

    – Joshua Nozzi
    Nov 7 '17 at 16:55





    Just to be clear, this wasn’t snarky criticism. I keep revisiting the problem of storing heterogenous collections to no avail. A generic solution is best, which means we can’t know the types at decoding time.

    – Joshua Nozzi
    Nov 7 '17 at 16:55













    In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

    – Tommie C.
    Nov 13 '17 at 13:30





    In Xcode under Help > Developer Documentation, search for a great article called "Encoding and Decoding Custom Types". I think reading that will help you.

    – Tommie C.
    Nov 13 '17 at 13:30













    I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

    – Natanel
    Jan 9 '18 at 17:23





    I'm trying to do this but I keep getting a runtime error upon encoding the data stored in an array. "Fatal error: Array<Parent> does not conform to Encodable because Parent does not conform to Encodable." Any help?

    – Natanel
    Jan 9 '18 at 17:23




    2




    2





    This isn't composition.

    – mxcl
    May 16 '18 at 18:28





    This isn't composition.

    – mxcl
    May 16 '18 at 18:28











    1














    Here is a library TypePreservingCodingAdapter to do just that (can be installed with Cocoapods or SwiftPackageManager).



    The code below compiles and works just fine with Swift 4.2. Unfortunately for every subclass you'll need to implement encoding and decoding of properties on your own.



    import TypePreservingCodingAdapter
    import Foundation

    // redeclared your types with initializers
    class Server: Codable {
    var id: Int?

    init(id: Int?) {
    self.id = id
    }
    }

    class Development: Server {
    var name: String?
    var userId: Int?

    private enum CodingKeys: String, CodingKey {
    case name
    case userId
    }

    init(id: Int?, name: String?, userId: Int?) {
    self.name = name
    self.userId = userId
    super.init(id: id)
    }

    required init(from decoder: Decoder) throws {
    try super.init(from: decoder)
    let container = try decoder.container(keyedBy: CodingKeys.self)

    name = try container.decodeIfPresent(String.self, forKey: .name)
    userId = try container.decodeIfPresent(Int.self, forKey: .userId)
    }

    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(userId, forKey: .userId)
    }

    }

    // create and adapter
    let adapter = TypePreservingCodingAdapter()
    let encoder = JSONEncoder()
    let decoder = JSONDecoder()

    // inject it into encoder and decoder
    encoder.userInfo[.typePreservingAdapter] = adapter
    decoder.userInfo[.typePreservingAdapter] = adapter

    // register your types with adapter
    adapter.register(type: Server.self).register(type: Development.self)


    let server = Server(id: 1)
    let development = Development(id: 2, name: "dev", userId: 42)

    let servers: [Server] = [server, development]

    // wrap specific object with Wrap helper object
    let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })

    // decode object back and unwrap them force casting to a common ancestor type
    let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }

    // check that decoded object are of correct types
    print(decodedServers.first is Server) // prints true
    print(decodedServers.last is Development) // prints true





    share|improve this answer






























      1














      Here is a library TypePreservingCodingAdapter to do just that (can be installed with Cocoapods or SwiftPackageManager).



      The code below compiles and works just fine with Swift 4.2. Unfortunately for every subclass you'll need to implement encoding and decoding of properties on your own.



      import TypePreservingCodingAdapter
      import Foundation

      // redeclared your types with initializers
      class Server: Codable {
      var id: Int?

      init(id: Int?) {
      self.id = id
      }
      }

      class Development: Server {
      var name: String?
      var userId: Int?

      private enum CodingKeys: String, CodingKey {
      case name
      case userId
      }

      init(id: Int?, name: String?, userId: Int?) {
      self.name = name
      self.userId = userId
      super.init(id: id)
      }

      required init(from decoder: Decoder) throws {
      try super.init(from: decoder)
      let container = try decoder.container(keyedBy: CodingKeys.self)

      name = try container.decodeIfPresent(String.self, forKey: .name)
      userId = try container.decodeIfPresent(Int.self, forKey: .userId)
      }

      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(userId, forKey: .userId)
      }

      }

      // create and adapter
      let adapter = TypePreservingCodingAdapter()
      let encoder = JSONEncoder()
      let decoder = JSONDecoder()

      // inject it into encoder and decoder
      encoder.userInfo[.typePreservingAdapter] = adapter
      decoder.userInfo[.typePreservingAdapter] = adapter

      // register your types with adapter
      adapter.register(type: Server.self).register(type: Development.self)


      let server = Server(id: 1)
      let development = Development(id: 2, name: "dev", userId: 42)

      let servers: [Server] = [server, development]

      // wrap specific object with Wrap helper object
      let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })

      // decode object back and unwrap them force casting to a common ancestor type
      let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }

      // check that decoded object are of correct types
      print(decodedServers.first is Server) // prints true
      print(decodedServers.last is Development) // prints true





      share|improve this answer




























        1












        1








        1







        Here is a library TypePreservingCodingAdapter to do just that (can be installed with Cocoapods or SwiftPackageManager).



        The code below compiles and works just fine with Swift 4.2. Unfortunately for every subclass you'll need to implement encoding and decoding of properties on your own.



        import TypePreservingCodingAdapter
        import Foundation

        // redeclared your types with initializers
        class Server: Codable {
        var id: Int?

        init(id: Int?) {
        self.id = id
        }
        }

        class Development: Server {
        var name: String?
        var userId: Int?

        private enum CodingKeys: String, CodingKey {
        case name
        case userId
        }

        init(id: Int?, name: String?, userId: Int?) {
        self.name = name
        self.userId = userId
        super.init(id: id)
        }

        required init(from decoder: Decoder) throws {
        try super.init(from: decoder)
        let container = try decoder.container(keyedBy: CodingKeys.self)

        name = try container.decodeIfPresent(String.self, forKey: .name)
        userId = try container.decodeIfPresent(Int.self, forKey: .userId)
        }

        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(userId, forKey: .userId)
        }

        }

        // create and adapter
        let adapter = TypePreservingCodingAdapter()
        let encoder = JSONEncoder()
        let decoder = JSONDecoder()

        // inject it into encoder and decoder
        encoder.userInfo[.typePreservingAdapter] = adapter
        decoder.userInfo[.typePreservingAdapter] = adapter

        // register your types with adapter
        adapter.register(type: Server.self).register(type: Development.self)


        let server = Server(id: 1)
        let development = Development(id: 2, name: "dev", userId: 42)

        let servers: [Server] = [server, development]

        // wrap specific object with Wrap helper object
        let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })

        // decode object back and unwrap them force casting to a common ancestor type
        let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }

        // check that decoded object are of correct types
        print(decodedServers.first is Server) // prints true
        print(decodedServers.last is Development) // prints true





        share|improve this answer















        Here is a library TypePreservingCodingAdapter to do just that (can be installed with Cocoapods or SwiftPackageManager).



        The code below compiles and works just fine with Swift 4.2. Unfortunately for every subclass you'll need to implement encoding and decoding of properties on your own.



        import TypePreservingCodingAdapter
        import Foundation

        // redeclared your types with initializers
        class Server: Codable {
        var id: Int?

        init(id: Int?) {
        self.id = id
        }
        }

        class Development: Server {
        var name: String?
        var userId: Int?

        private enum CodingKeys: String, CodingKey {
        case name
        case userId
        }

        init(id: Int?, name: String?, userId: Int?) {
        self.name = name
        self.userId = userId
        super.init(id: id)
        }

        required init(from decoder: Decoder) throws {
        try super.init(from: decoder)
        let container = try decoder.container(keyedBy: CodingKeys.self)

        name = try container.decodeIfPresent(String.self, forKey: .name)
        userId = try container.decodeIfPresent(Int.self, forKey: .userId)
        }

        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(userId, forKey: .userId)
        }

        }

        // create and adapter
        let adapter = TypePreservingCodingAdapter()
        let encoder = JSONEncoder()
        let decoder = JSONDecoder()

        // inject it into encoder and decoder
        encoder.userInfo[.typePreservingAdapter] = adapter
        decoder.userInfo[.typePreservingAdapter] = adapter

        // register your types with adapter
        adapter.register(type: Server.self).register(type: Development.self)


        let server = Server(id: 1)
        let development = Development(id: 2, name: "dev", userId: 42)

        let servers: [Server] = [server, development]

        // wrap specific object with Wrap helper object
        let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })

        // decode object back and unwrap them force casting to a common ancestor type
        let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }

        // check that decoded object are of correct types
        print(decodedServers.first is Server) // prints true
        print(decodedServers.last is Development) // prints true






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 30 at 16:25

























        answered Jan 30 at 16:05









        Igor MuzykaIgor Muzyka

        17629




        17629






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f44553934%2fusing-decodable-in-swift-4-with-inheritance%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Xamarin.iOS Cant Deploy on Iphone

            Glorious Revolution

            Dulmage-Mendelsohn matrix decomposition in Python