Swift 4 Codable Realm Object Subclasses

大城市里の小女人 提交于 2020-01-05 06:38:10

问题


trying to switch some of my codebase over to Swift 4's new nifty Codable protocol. My setup looks something like this:

class Base: Object, Codable {

    dynamic var id: String = ""
    dynamic var timestamp: String = ""

    private enum CodingKeys: String, CodingKey {

        case id = "_id"
        case timestamp = "timestamp"

    }

}

class User: Base {

    dynamic var name: String = ""

    private enum CodingKeys: String, CodingKey {
        case name = "name"
    }

    required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        try super.init(from: decoder)

    }

}

I have a base realm object class that conforms to Codable, and a subclass of Base that also has it's own coding keys. I override init(decoder:) on the User subclass to map my additional coding keys, then call super.init(decoder:) to map Base's coding keys. However, once I override init(decoder:) I get the following errors:

  • required initializer 'init()' must be provided by subclass of 'Base'
  • required initializer 'init(realm:schema:)' must be provided by subclass of 'Base'
  • required initializer 'init(value:schema:)' must be provided by subclass of 'Base'

I'm not sure what the correct way is to go about fixing these issues.


回答1:


You cannot override init() or other initializers of Realm Object. You can use convenience initializer instead. Then you cannot call super.init(from:), so define a method that decodes superclass' properties like Base.decode(from:).

See following code sample:

class Base: Object, Codable {
    dynamic var id: String = ""
    dynamic var timestamp: String = ""

    private enum CodingKeys: String, CodingKey {
        case id = "_id"
        case timestamp = "timestamp"
    }

    func decode(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.timestamp = try container.decode(String.self, forKey: .timestamp)
    }
}

class User: Base {
    dynamic var name: String = ""

    private enum CodingKeys: String, CodingKey {
        case id = "_id"
        case timestamp
        case name = "name"
    }

    required convenience init(from decoder: Decoder) throws {
        self.init()

        try decode(from: decoder)

        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    }
}



回答2:


You cannot override just one initializer of a class. If you are going to override, do it for all of them. If you don't really use or care about the other initializers just at least call super.<respective init> in them.
For init(realm: RLMRealm, schema: RLMObjectSchema) and init(value: Any, schema: RLMSchema) compiler is going to complain that it doesn't know what RLMRealm, RLMObjectSchema and RLMSchema are. So import Realm besides RealmSwift.




回答3:


As I just answered on another question, whilst you may be able to use your subclass as a Codable type with Katsumi's advice above, you may run into another gotcha.

You cannot have collections of Base as a reference type that contain subclass instances and have that collection survive Codable. It will only decode as Base instances.

Polymorphic persistence appears to be broken by design.

The bug report SR-5331 quotes the response they got on their Radar.

Unlike the existing NSCoding API (NSKeyedArchiver), the new Swift 4 Codable implementations do not write out type information about encoded types into generated archives, for both flexibility and security. As such, at decode time, the API can only use the concrete type your provide in order to decode the values (in your case, the superclass type).

This is by design — if you need the dynamism required to do this, we recommend that you adopt NSSecureCoding and use NSKeyedArchiver/NSKeyedUnarchiver

I am unimpressed, having thought from all the glowing articles that Codable was the answer to some of my prayers. A parallel set of Codable structs that act as object factories is one workaround I'm considering, to preserve type information.



来源:https://stackoverflow.com/questions/47080785/swift-4-codable-realm-object-subclasses

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!