使用可选类型时,RealmSwift和Codable

时间:2018-08-17 14:15:09

标签: swift swift3 realm codable

我在项目中使用RealmSwift和Codable已有很长时间了,但是,我的api开发人员只给了我两个新属性,它们仅在对象的某些返回调用中存在。如果我致电getUserInfo,则会收到没有这两个属性的user model。在这种情况下,您将在可编码中使用decodeIfPresent,并将数据类型设置为可选。但是,这两个字段是纪元时间值,使它们成为某种数字。域要求您的数据类型以@objc为前缀。

@objc dynamic var scanIn:Double = 0

当然,所有数字原语都是这样工作的,但是其中的NONE是可选的。您必须使用NSNumber或类似的方法来对ObjC使用可选参数,但是幸运的是,CodableNSNumber不兼容。我知道我在这里有很多不同的选择,但是我确实在寻找一种简单快捷的方法,不需要我通过映射来重建整个模型或在收到模型时进行转换。我现在将写出一种解决方法,但我真的很想让事情简单明了。

如果没有返回值,我尝试设置一个值,并且只使用像这样的非可选类型

scanIn = try container.decodeIfPresent(Double.self, forKey:.scanIn) ?? 0

但是,由于某种原因,这会将ALL值设置为0。我不知道为什么要这么做,但是另一个开发人员建议它不能像这样在可编码中工作,因此我不得不将double设置为optional。我想澄清一下,此数字在转换之前立即存在,但在转换之后为0。

有什么容易解决的想法吗?也许我做错了什么?

2 个答案:

答案 0 :(得分:4)

您可以使用RealmOptional<Double>类型。

documentation中所述:

  

使用RealmOptional类型声明可选的数字类型:

let age = RealmOptional<Int>()
  

RealmOptional支持IntFloatDoubleBool以及Int({{1} },Int8Int16Int32)。

答案 1 :(得分:0)

Vin Gazoli给了我丢失的钥匙。

首先,需要将RealmOptional声明为let,因此在初始化中,您需要使用myObject.myVariable.value = newValue来设置值。然后,在任何使用它的地方都必须将它也用作obj.variable.value。但是,RealmOptional不符合可编码要求,因此您必须编写一个扩展名。您可以在下面找到它以及指向我收到它的地方的链接。

对象外:

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
}

以下代码是实现上述对象功能所必需的。在该页面的注释中与h1m5的修复一起检索了from this link。以下是Double。该链接还有其他原语。

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
    }
}
}