如何使RealmSwift RealmOptional与Swift Codable兼容



我面临一个问题,我无法使RealmOptional与带有json解码器的swift新Codable功能兼容。

想象下面的境界对象。

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,因为我在这个要点的帮助下为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)

但是,如果我从json数据中删除numberOfStudents密钥,它将抛出一个错误,并且不会解码,因为RealmOptional不是一个swift可选对象,所以解码器认为json数据应该有一个密钥。在JSONDecoder中,如果json中没有密钥并且属性被声明为可选,则不会尝试解码。它只是简单地跳到其他键。

到目前为止,我没有覆盖初始化程序,因为我们有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兼容,这样我们就不必覆盖初始化程序了。

以下是解决该问题的方法。创建一个支持解码并以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。

我修改了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>?
  1. 在Realm Model类之上添加@objcMembers。

  2. 使用变量如下

public dynamic var someValue = RealmOptional<Int>()

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

默认情况下,someValue将为零。

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

自去年以来,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?
}

相关内容

  • 没有找到相关文章

最新更新