8

我面临一个问题,我无法使 RealmOptional 与带有 json 解码器的 swift 新 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,因为我在这个 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)

但是,如果我numberOfStudents从应该是 RealmOptional 对象的 json 数据中删除密钥,它将引发错误并且不会解码,因为 RealmOptional 不是 swift 可选的,因此解码器认为 json 数据中应该有一个密钥。如果JSONDecoderjson 中不存在密钥并且该属性被声明为可选,则它不会尝试解码。它只是跳到其他键。

到目前为止,我没有覆盖初始化程序,因为我们拥有RealmOptionalRealmLists等的所有支持扩展。但是现在我必须覆盖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

5 回答 5

7

您可以采取以下措施来解决此问题。创建一个支持解码并具有 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
    }
}

请注意,现在不是使用 RealmOptional,而是使用 RealmNumeric?这是可选的类型。由于它是可选的,因此自动解码使用 decodeIfPresent 方法来解码可选值。如果它不存在于 json 中,则该值将简单地变为 nil。

于 2018-09-03T19:21:41.670 回答
2

我已将 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>?
于 2020-01-12T12:21:35.513 回答
1
  1. 在您的领域模型类上方添加@objcMembers。

  2. 使用如下变量

public dynamic var someValue = RealmOptional<Int>()

  1. 在将值分配给领域可选时,您可以使用 someValue.value = 10

默认情况下 someValue 将为 nil。

于 2019-03-14T11:22:12.070 回答
0

我找到了这个解决方案,它就像一个魅力。我正在使用 srv7评论中的更新代码。

于 2018-08-09T17:52:43.800 回答
0

自去年以来,Realm 为可选选项添加了一种新的更简单的方法,使用@Persisted- docs

如何使用它:

class Order: Object, Codable {

   @Persisted(primaryKey: true) var productOrderId: Int?

   @Persisted var name: String?
   @Persisted var standardPrice: Double?
   @Persisted var paid: Bool?

}
于 2022-02-09T14:52:16.327 回答