如何使 RealmSwift RealmOptional 与 Swift Codable 兼容?
How to make the RealmSwift RealmOptional compatible with Swift Codable?
我遇到了一个问题,我无法使 RealmOptional 与 swift 带有 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>()
}
在这里我们可以将 class 声明为 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 不是 swift 可选,因此解码器认为 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
兼容,这样我们就不必覆盖初始化程序。
我找到了 this solution and it works like a charm. I am using the updated code from srv7's comment。
您可以执行以下操作来解决此问题。创建一个新的 class,它支持解码,并以 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 class,而是使用这个新的 OptionalInt64 class,
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。
在您的领域模型上方添加@objcMembers class。
使用如下变量
public dynamic var someValue = RealmOptional<Int>()
- 在可选领域赋值时,您可以使用 someValue.value = 10
默认情况下,someValue 将为 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>?
自去年以来,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?
}
我遇到了一个问题,我无法使 RealmOptional 与 swift 带有 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>()
}
在这里我们可以将 class 声明为 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 不是 swift 可选,因此解码器认为 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
兼容,这样我们就不必覆盖初始化程序。
我找到了 this solution and it works like a charm. I am using the updated code from srv7's comment。
您可以执行以下操作来解决此问题。创建一个新的 class,它支持解码,并以 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 class,而是使用这个新的 OptionalInt64 class,
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。
在您的领域模型上方添加@objcMembers class。
使用如下变量
public dynamic var someValue = RealmOptional<Int>()
- 在可选领域赋值时,您可以使用 someValue.value = 10
默认情况下,someValue 将为 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>?
自去年以来,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?
}