使用可选类型时的 RealmSwift 和 Codable
RealmSwift and Codable when using optional types
我在一个项目中使用 RealmSwift
和 Codable 已经很长时间了,但是,我的 api 开发人员刚刚给了我两个只存在于某些 return 上的新属性对象的调用。如果我调用 getUserInfo
,我会收到没有这两个属性的 user model
。在这种情况下,您可以在 codable 中使用 decodeIfPresent 并将数据类型设置为可选。然而,这两个字段是纪元时间值,使它们成为某种数字。 Realm 要求您的数据类型以 @objc
.
为前缀
@objc dynamic var scanIn:Double = 0
当然,所有数字原语都是这样工作的,但是 NONE
其中 NONE
作为可选值工作。您必须使用 NSNumber
或类似的方式来使用 ObjC
的可选值,但幸运的是,Codable
不适用于 NSNumber
。我知道我在这里有很多不同的选择,但我真的在寻找简单快捷的东西,不需要我用映射重建整个模型或在收到它时转换所有内容。我现在会写一个解决方法,但我真的想让事情简单明了。
我尝试设置一个值 if none return 并且只使用像这样的非可选类型
scanIn = try container.decodeIfPresent(Double.self, forKey:.scanIn) ?? 0
但是,出于某种原因,这会将所有值设置为 0。我不知道它为什么这样做,但另一个开发人员建议它不能像那样在 codable 中工作,我不得不将 double 设置为可选。我想澄清一下,这个数字在转换前立即存在,但转换后为 0。
有什么简单的解决方法吗?也许我做错了什么?
您可以使用 RealmOptional<Double>
类型。
如 documentation 中所述:
Optional numeric types are declared using the RealmOptional
type:
let age = RealmOptional<Int>()
RealmOptional
supports Int
, Float
, Double
, Bool
, and all of the sized versions of Int
(Int8
, Int16
, Int32
, Int64
).
Vin Gazoli 给了我丢失的钥匙。
首先,RealmOptional需要声明为let,所以在init中需要使用myObject.myVariable.value = newValue
来设置值。然后,无论你在哪里使用它,你都必须将它用作 obj.variable.value。 RealmOptional 不符合 codable,所以你必须写一个扩展。您可以在下面找到它,也可以在 link 我收到它的地方找到它。
对象的例子:
class Attendee: Object,Codable {
@objc dynamic var id = 0
@objc dynamic var attendeeId = 0
@objc dynamic var lanId = ""
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var email = ""
@objc dynamic var employeeId = ""
@objc dynamic var badgeId = ""
@objc dynamic var department = ""
@objc dynamic var picture:String? = nil
let scanIn = RealmOptional<Double>()
let scanOut = RealmOptional<Double>()
override static func primaryKey () -> String? {
return "id"
}
private enum CodingKeys: String, CodingKey {
case id, attendeeId, lanId, firstName, lastName, email, employeeId, badgeId, department, picture, scanIn, scanOut
}
required convenience init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey:.id)
attendeeId = try container.decodeIfPresent(Int.self, forKey:.attendeeId) ?? 0
lanId = try container.decode(String.self, forKey:.lanId)
firstName = try container.decode(String.self, forKey:.firstName)
lastName = try container.decode(String.self, forKey:.lastName)
email = try container.decode(String.self, forKey:.email)
employeeId = try container.decode(String.self, forKey:.employeeId)
badgeId = try container.decode(String.self, forKey:.badgeId)
department = try container.decode(String.self, forKey:.department)
picture = try container.decodeIfPresent(String.self, forKey:.picture)
self.scanIn.value = try container.decodeIfPresent(Double.self, forKey:.scanIn) ?? 0
self.scanOut.value = try container.decodeIfPresent(Double.self, forKey:.scanOut) ?? 0
}
需要以下代码才能实现上述对象功能。在该页面的评论中检索 from this link 连同 h1m5 的修复。以下是双人。 link 还有其他原语。
func assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Encodable.Type else {
if T.self == Encodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
}
}
}
func assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Decodable.Type else {
if T.self == Decodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
}
}
}
extension RealmOptional : Encodable where Value : Encodable {
public func encode(to encoder: Encoder) throws {
assertTypeIsEncodable(Value.self, in: type(of: self))
var container = encoder.singleValueContainer()
if let v = self.value {
try (v as Encodable).encode(to: encoder) // swiftlint:disable:this force_cast
} else {
try container.encodeNil()
}
}
}
extension RealmOptional : Decodable where Value : Decodable {
public convenience init(from decoder: Decoder) throws {
// Initialize self here so we can get type(of: self).
self.init()
assertTypeIsDecodable(Value.self, in: type(of: self))
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
let metaType = (Value.self as Decodable.Type) // swiftlint:disable:this force_cast
let element = try metaType.init(from: decoder)
self.value = (element as! Value) // swiftlint:disable:this force_cast
}
}
}
我在一个项目中使用 RealmSwift
和 Codable 已经很长时间了,但是,我的 api 开发人员刚刚给了我两个只存在于某些 return 上的新属性对象的调用。如果我调用 getUserInfo
,我会收到没有这两个属性的 user model
。在这种情况下,您可以在 codable 中使用 decodeIfPresent 并将数据类型设置为可选。然而,这两个字段是纪元时间值,使它们成为某种数字。 Realm 要求您的数据类型以 @objc
.
@objc dynamic var scanIn:Double = 0
当然,所有数字原语都是这样工作的,但是 NONE
其中 NONE
作为可选值工作。您必须使用 NSNumber
或类似的方式来使用 ObjC
的可选值,但幸运的是,Codable
不适用于 NSNumber
。我知道我在这里有很多不同的选择,但我真的在寻找简单快捷的东西,不需要我用映射重建整个模型或在收到它时转换所有内容。我现在会写一个解决方法,但我真的想让事情简单明了。
我尝试设置一个值 if none return 并且只使用像这样的非可选类型
scanIn = try container.decodeIfPresent(Double.self, forKey:.scanIn) ?? 0
但是,出于某种原因,这会将所有值设置为 0。我不知道它为什么这样做,但另一个开发人员建议它不能像那样在 codable 中工作,我不得不将 double 设置为可选。我想澄清一下,这个数字在转换前立即存在,但转换后为 0。
有什么简单的解决方法吗?也许我做错了什么?
您可以使用 RealmOptional<Double>
类型。
如 documentation 中所述:
Optional numeric types are declared using the
RealmOptional
type:
let age = RealmOptional<Int>()
RealmOptional
supportsInt
,Float
,Double
,Bool
, and all of the sized versions ofInt
(Int8
,Int16
,Int32
,Int64
).
Vin Gazoli 给了我丢失的钥匙。
首先,RealmOptional需要声明为let,所以在init中需要使用myObject.myVariable.value = newValue
来设置值。然后,无论你在哪里使用它,你都必须将它用作 obj.variable.value。 RealmOptional 不符合 codable,所以你必须写一个扩展。您可以在下面找到它,也可以在 link 我收到它的地方找到它。
对象的例子:
class Attendee: Object,Codable {
@objc dynamic var id = 0
@objc dynamic var attendeeId = 0
@objc dynamic var lanId = ""
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var email = ""
@objc dynamic var employeeId = ""
@objc dynamic var badgeId = ""
@objc dynamic var department = ""
@objc dynamic var picture:String? = nil
let scanIn = RealmOptional<Double>()
let scanOut = RealmOptional<Double>()
override static func primaryKey () -> String? {
return "id"
}
private enum CodingKeys: String, CodingKey {
case id, attendeeId, lanId, firstName, lastName, email, employeeId, badgeId, department, picture, scanIn, scanOut
}
required convenience init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey:.id)
attendeeId = try container.decodeIfPresent(Int.self, forKey:.attendeeId) ?? 0
lanId = try container.decode(String.self, forKey:.lanId)
firstName = try container.decode(String.self, forKey:.firstName)
lastName = try container.decode(String.self, forKey:.lastName)
email = try container.decode(String.self, forKey:.email)
employeeId = try container.decode(String.self, forKey:.employeeId)
badgeId = try container.decode(String.self, forKey:.badgeId)
department = try container.decode(String.self, forKey:.department)
picture = try container.decodeIfPresent(String.self, forKey:.picture)
self.scanIn.value = try container.decodeIfPresent(Double.self, forKey:.scanIn) ?? 0
self.scanOut.value = try container.decodeIfPresent(Double.self, forKey:.scanOut) ?? 0
}
需要以下代码才能实现上述对象功能。在该页面的评论中检索 from this link 连同 h1m5 的修复。以下是双人。 link 还有其他原语。
func assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Encodable.Type else {
if T.self == Encodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
}
}
}
func assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Decodable.Type else {
if T.self == Decodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
}
}
}
extension RealmOptional : Encodable where Value : Encodable {
public func encode(to encoder: Encoder) throws {
assertTypeIsEncodable(Value.self, in: type(of: self))
var container = encoder.singleValueContainer()
if let v = self.value {
try (v as Encodable).encode(to: encoder) // swiftlint:disable:this force_cast
} else {
try container.encodeNil()
}
}
}
extension RealmOptional : Decodable where Value : Decodable {
public convenience init(from decoder: Decoder) throws {
// Initialize self here so we can get type(of: self).
self.init()
assertTypeIsDecodable(Value.self, in: type(of: self))
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
let metaType = (Value.self as Decodable.Type) // swiftlint:disable:this force_cast
let element = try metaType.init(from: decoder)
self.value = (element as! Value) // swiftlint:disable:this force_cast
}
}
}