Как сделать RealmSwift RealmOptional совместимым со Swift Codable?

Я столкнулся с проблемой, из-за которой я не могу сделать RealmOptional совместимым с быстрой новой функцией Codable с декодером json.

Cosider следующий объект 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, потому что я написал расширение для 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 из данных json, которые должны быть объектом RealmOptional, он выдаст ошибку и не будет декодировать, потому что RealmOptional не является быстрым необязательным, поэтому декодер считает, что в данных json должен быть ключ. В JSONDecoder он не пытается декодировать, если ключ отсутствует в json, а свойство объявлено как необязательное. Он просто переходит к другим клавишам.

До сих пор я не отменял инициализатор, потому что у нас были все поддерживающие расширения для RealmOptional Realm 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, чтобы нам не пришлось переопределять инициализаторы.


person Shreesha Kedlaya    schedule 12.07.2018    source источник


Ответы (4)


Вот что вы можете сделать, чтобы обойти проблему. Создайте новый класс, который поддерживает декодирование и имеет 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, значение просто станет равным нулю.

person Sandeep    schedule 03.09.2018

Я изменил решение 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>?
person Mickael Belhassen    schedule 12.01.2020
comment
Как нам присвоить ценность? wr.integer = RealmOptionalCodable (значение: 21) аварийно завершает работу - person Salmaan; 14.01.2020
comment
Вы должны назначить так: numberOfStudents.value = 21 - person Mickael Belhassen; 14.01.2020

  1. Добавьте @objcMembers над классом модели Realm.

  2. Используйте переменную, как показано ниже

public dynamic var someValue = RealmOptional<Int>()

  1. При назначении значений области необязательно, вы можете использовать someValue.value = 10

По умолчанию someValue будет равен нулю.

person Swaroop S    schedule 14.03.2019

Я нашел это решение, и оно отлично работает. Я использую обновленный код из комментария srv7.

person Tom Daniel    schedule 09.08.2018
comment
У меня уже есть этот код в моей кодовой базе. Я просто хотел использовать RealmOptional без переопределения инициализатора. Поскольку RealmOptional не является быстрым необязательным, JSONDecoder считает, что в json должна быть пара ключ-значение, и пытается декодировать код. Вот почему выдает ошибку. Я хочу решить эту проблему. Спасибо. - person Shreesha Kedlaya; 10.08.2018
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - person user28434'mstep; 15.01.2019