简体   繁体   English

Swift 4 Codable Realm对象子类

[英]Swift 4 Codable Realm Object Subclasses

trying to switch some of my codebase over to Swift 4's new nifty Codable protocol. 试图将我的一些代码库转换为Swift 4的新的漂亮的Codable协议。 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. 我有一个符合Codable的基础领域对象类,以及一个Base的子类,它也有自己的编码密钥。 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. 我覆盖User子类上的init(decoder:)来映射我的附加编码密钥,然后调用super.init(decoder:)来映射Base的编码密钥。 However, once I override init(decoder:) I get the following errors: 但是,一旦我覆盖init(decoder:)我会收到以下错误:

  • required initializer 'init()' must be provided by subclass of 'Base' 必需的初始化程序'init()'必须由'Base'的子类提供
  • required initializer 'init(realm:schema:)' must be provided by subclass of 'Base' 必需的初始化程序'init(realm:schema :)'必须由'Base'的子类提供
  • required initializer 'init(value:schema:)' must be provided by subclass of 'Base' 必需的初始化程序'init(value:schema :)'必须由'Base'的子类提供

I'm not sure what the correct way is to go about fixing these issues. 我不确定解决这些问题的正确方法是什么。

You cannot override init() or other initializers of Realm Object . 您不能覆盖Realm Object init()或其他初始值设定项。 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:) . 然后你不能调用super.init(from:) ,所以定义一个解码超类'属性的方法,如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)
    }
}

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. 如果你没有真正使用或关心其他初始化器,那么至少要调用super.<respective init>
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. 对于init(realm: RLMRealm, schema: RLMObjectSchema)init(value: Any, schema: RLMSchema)编译器会抱怨它不知道RLMRealm,RLMObjectSchema和RLMSchema是什么。 So import Realm besides RealmSwift. 因此除了RealmSwift之外还要导入Realm。

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. 正如我刚刚回答的另一个问题,虽然您可以使用上面的Katsumi建议将您的子类用作Codable类型,但您可能遇到另一个问题。

You cannot have collections of Base as a reference type that contain subclass instances and have that collection survive Codable. 您不能将Base集合作为包含子类实例的引用类型,并使该集合在Codable中存在。 It will only decode as Base instances. 它只会解码为Base实例。

Polymorphic persistence appears to be broken by design . 多态持久性似乎被设计破坏了

The bug report SR-5331 quotes the response they got on their Radar. 错误报告SR-5331引用了他们对雷达的反应

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. 与现有的NSCoding API(NSKeyedArchiver)不同,新的Swift 4 Codable实现不会将编码类型的类型信息写入生成的归档中,以实现灵活性和安全性。 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). 因此,在解码时,API只能使用您提供的具体类型来解码值(在您的情况下,超类类型)。

This is by design — if you need the dynamism required to do this, we recommend that you adopt NSSecureCoding and use NSKeyedArchiver/NSKeyedUnarchiver 这是设计 - 如果您需要执行此操作所需的动态,我们建议您采用NSSecureCoding并使用NSKeyedArchiver / NSKeyedUnarchiver

I am unimpressed, having thought from all the glowing articles that Codable was the answer to some of my prayers. 我没有打动,从所有发光文章中想到,Codable是我祈祷的答案。 A parallel set of Codable structs that act as object factories is one workaround I'm considering, to preserve type information. 作为对象工厂的一组并行的Codable结构是我正在考虑的一种解决方法,用于保存类型信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM