How to decode an array of inherited classes in Swift

前端 未结 1 1271
有刺的猬
有刺的猬 2021-01-24 03:56

The problem: decode an array of objects belonging to Parent and Child classes.

I read a lot of stuff on this subject but I have not been able to find a simple solution.

相关标签:
1条回答
  • 2021-01-24 04:49

    I think a major part of the problem is that you are using an array of mixed types, [Any], and then you are decoding it as one type Parent because it is quite possible to get the child objects to be properly encoded as Child.

    One solution is to create a new Codable struct that holds the array and that with the use of a type property keeps track on how to decode the objects in the array

    enum ObjectType: String, Codable {
        case parent
        case child
    }
    
    struct ParentAndChild: Codable {
        let objects: [Parent]
    
        enum CodingKeys: CodingKey {
            case objects
        }
    
        enum ObjectTypeKey: CodingKey {
            case type
        }
    
        init(with objects: [Parent]) {
            self.objects = objects
        }
    
        init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: CodingKeys.self)
                var objectsArray = try container.nestedUnkeyedContainer(forKey: CodingKeys.objects)
                var items = [Parent]()
    
            var array = objectsArray
            while !objectsArray.isAtEnd {
                let object = try objectsArray.nestedContainer(keyedBy: ObjectTypeKey.self)
                let type = try object.decode(ObjectType.self, forKey: ObjectTypeKey.type)
                switch type {
                case .parent:
                    items.append(try array.decode(Parent.self))
                case .child:
                    items.append(try array.decode(Child.self))
                }
            }
            self.objects = items
        }
    }
    

    I have also made some changes to the classes as well, the Parent class is hugely simplified and the Child class has modified functionality for encoding/decoding where the main change is that init(from:) calls supers init(from:)

    class Parent: Codable, CustomDebugStringConvertible {
        var debugDescription: String {
            return "[\(name)]"
        }
    
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    class Child: Parent {
    
        override var debugDescription: String {
            return "[\(name) - \(age)]"
        }
        var age: Int
    
        init(name: String, age: Int) {
            self.age = age
            super.init(name: name)
        }
    
        enum CodingKeys: CodingKey {
            case age
        }
    
        required init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            age = try container.decode(Int.self, forKey: .age)
            try super.init(from: decoder)
        }
    
        override func encode(to encoder: Encoder) throws {
            try super.encode(to: encoder)
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(age, forKey: .age)
        }
    }
    
    0 讨论(0)
提交回复
热议问题