如何使RealmSwift RealmOptional与Swift Codable兼容?

时间:2018-07-12 09:31:04

标签: swift realm codable jsondecoder

我遇到了一个问题,即我无法使RealmOptional与具有json解码器的迅速的新Codable功能兼容。

考虑以下Realm对象。

class School: Object, Codable {

    @objc dynamic var id: Int64 = 0

    @objc dynamic var name: String?
    var numberOfStudents = RealmOptional<Int64>()
    var classes = List<Class>()

    enum CodingKeys: String, CodingKey {
       case id
       case name
       case numberOfStudents
       case classes
    }
}

class Class: Object, Codable {
    var name: String?
    var numberOfStudents = RealmOptional<Int64>()
}

在这里我们可以将类声明为Codable,因为我在this gist的帮助下为RealmOptinal编写了扩展名。但是问题在于解码器何时解码json。

考虑这个json

let jsonData = """
[
    "id": 1234,
    "name": "Shreesha",
    "numberOfStudents": nil,
    "classes": {
       "name": "Class V",
       "numberOfStudents": 12
    }
]
""".data(using: .utf8)!

在此json中传递所有数据,并使用代码完美解码。

let decoder = JSONDecoder()

let decoded = try! decoder.decode(School.self, from: jsonData)

但是,如果我从应该是RealmOptional对象的json数据中删除了numberOfStudents键,则会抛出错误并且不会解码,因为RealmOptional不是快速可选的,因此解码器认为应该json数据中的键。在JSONDecoder中,如果密钥不在json中并且该属性被声明为可选,则它不会尝试解码。它只是跳到其他键。

直到现在我还没有覆盖初始化器,因为我们拥有RealmOptional领域Lists等的所有支持扩展。但是现在我不得不覆盖init(from decoder: Decoder)才能手动对其进行解码, Realm模型中有超过 50 个属性(您知道我的意思)。

如果我们重写了初始化程序,我觉得使用JSONDecoder是没有意义的,因为与使用JSONDecoder相比,手动工作更多。

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

    id = try container.decodeIfPresent(Int64.self, forKey: .id) ?? 0
    name = try container.decodeIfPresent(String?.self, forKey: .name) ?? ""
    numberOfStudents = try container.decodeIfPresent(RealmOptional<Int64>.self, forKey: .numberOfStudents) ?? RealmOptional<Int64>()

    let classesArray = try container.decode([Class].self, forKey: .classes)
    classes.append(objectsIn: classesArray)
}

所以有人可以建议我替代方案,使RealmOptional与JSONDecoder兼容,这样我们就不必覆盖初始化程序。

4 个答案:

答案 0 :(得分:1)

这是您可以解决此问题的方法。创建一个支持解码并具有RealmOptional作为其属性的新类。

class OptionalInt64: Object, Decodable {
    private var numeric = RealmOptional<Int64>()

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

        let singleValueContainer = try decoder.singleValueContainer()
        if singleValueContainer.decodeNil() == false {
            let value = try singleValueContainer.decode(Int64.self)
            numeric = RealmOptional(value)
        }
    }

    var value: Int64? {
        return numeric.value
    }

    var zeroOrValue: Int64 {
        return numeric.value ?? 0
    }
}

然后,不要在您的学校课堂上使用RealmOptional,而是使用这个新的OptionalInt64课堂,

class School: Object, Codable {

    @objc dynamic var id: Int64 = 0

    @objc dynamic var name: String?
    @objc dynamic  var numberOfStudents: OptionalInt64?
    var classes = List<Class>()

    enum CodingKeys: String, CodingKey {
       case id
       case name
       case numberOfStudents
       case classes
    }
}

请注意,现在您正在使用RealmNumeric而不是使用RealmOptional?类型为Optional。由于它是可选的,因此自动解码使用defineIfPresent方法解码可选值。而且,如果json中不存在该值,则该值将变为nil。

答案 1 :(得分:1)

我将Sandeep的解决方案修改为更通用:

class RealmOptionalCodable<Value: Codable>: Object, Codable where Value: RealmSwift.RealmOptionalType {

    private var numeric = RealmOptional<Value>()

    var value: Value? {
        get {
            numeric.value
        }
        set {
            numeric.value = newValue
        }
    }


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

        let singleValueContainer = try decoder.singleValueContainer()
        if singleValueContainer.decodeNil() == false {
            let value = try singleValueContainer.decode(Value.self)
            numeric = RealmOptional(value)
        }
    }

}

使用

@objc dynamic  var numberOfStudents: RealmOptionalCodable<Int>?

答案 2 :(得分:0)

我找到了this解决方案,它的工作原理很吸引人。我正在使用srv7的comment中更新的代码。

答案 3 :(得分:0)

  1. 在Realm Model类上方添加@objcMembers。

  2. 使用以下变量

public dynamic var someValue = RealmOptional<Int>()

  1. 为可选领域分配值时,可以使用someValue.value = 10

默认情况下someValue将为零。