Realm iOS - Как обновить список? - PullRequest
1 голос
/ 04 марта 2020

Я использую Codable with Realm для iOS и обнаружил проблему с обновлением существующих записей, сохраненных в Realm. Данные состоят из списка элементов, каждый из которых имеет список категорий, а каждая категория имеет описание.

Так выглядят мои модели Realm Codable

class Items: Object, Decodable {

    @objc dynamic var id: String = ""
    @objc dynamic var name: String = ""
    let categories =  List<Categories>()
    @objc dynamic var desc: Desc?

    override static func primaryKey() -> String? {
        return "id"
    }

    var hasSubCategories: Bool {
        if self.categories.count > 0 {
            return true
        }
        return false
    }

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case desc
        case categories
    }

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)

        id = try container.decode(String.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        desc = try (container.decodeIfPresent(Desc, forKey: .desc))
        let categoriesList = try container.decodeIfPresent(List<Categories>.self, forKey: .categories) ?? List<Categories>()
        categories.append(objectsIn: categoriesList)
        super.init()
    }
}

class Categories: Object, Decodable {

    @objc dynamic var id: String = ""
    @objc dynamic var name: String = ""
    @objc dynamic var desc: Desc?
    let categories =  List<Categories>()

    override static func primaryKey() -> String? {
        return "id"
    }

    var hasSubCategories: Bool {
        if categories.count > 0 {
            return true
        }
        return false
    }

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case categories
   }

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)

        id = try container.decode(String.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        desc = try (container.decodeIfPresent(Desc, forKey: . desc))
        let categoriesList = try container.decodeIfPresent(List<Categories>.self, forKey: .categories) ?? List<Categories>()
        categories.append(objectsIn: categoriesList)
        super.init()
    }
}

Я добавляю новые элементы, полученные из API, со следующим кодом

let realm = try? Realm()

 for item in ItemsFromAPI {

do {
     try realm?.write {
         realm?.add(item)
       }
    }
}

Когда данные уже сохранены в БД получен от API, мне нужно его обновить. Насколько я понимаю, с primarykey реализовано

realm?.add(Item, update: .modified)

обновит существующую запись, которая имеет тот же основной. Свойства объекта с классом Item обновляются, но список <> категорий не обновляется.

Я попытался получить существующий список категорий, сохраненных в базе данных, и изменил объект категории, связанный с saveItem, вызвав savedItem.categories.removeAll() и добавил новые категории с помощью savedItem.categories.append(objectsIn: itemFromAPI.categories) Когда я сохраняю это с помощью

realm?.add(Item, update: .modified)

Сбой области, выдавая исключение - «Причина RLMException: Попытка создать объект типа с существующим значением первичного ключа»

Поэтому я попытался удалить категорию с помощью

realm?.delete(Category)

, а затем добавил измененную категорию, связанную с элементом, и это работает.

Я вложил категории в несколько уровней и обновил их путем выборки. это снова, удаление их и чтение их снова оказывается болью. Так ли работает обновление записей в области?

Можно ли обновить вложенную категорию, связанную с элементом, просто назначив новый объект для элемента и используя

realm?.add(Item, update: .modified) 

для обновления записей как это сделано для добавления нового элемента?

Буду признателен за любой ответ.

1 Ответ

0 голосов
/ 05 марта 2020

За комментарий вопрос

Мой вопрос заключается в том, как обновить свойство списка областей с помощью функции realm? .Add (Item, update: .modified).

Позвольте мне повторить вопрос для ясности

Как обновить объект, сохраненный в другом объекте Свойство списка, где хранимые объекты в списке имеют первичный ключ

Краткий ответ: объект List содержит ссылки на другие объекты в этом списке. Поскольку он содержит ссылки, сам список не нуждается в обновлении, просто объект, на который он ссылается

Длинный ответ дан на примере: давайте возьмем Person, у которого свойство List объекта Dogs

class PersonClass: Object {
   @objc dynamic var person_id = UUID().uuidString
   let dogs = List<DogClass>()

    override static func primaryKey() -> String? {
        return "person_id"
    }
}

class DogClass: Object {
   @objc dynamic var dog_id = NSUUID().uuidString
   @objc dynamic var dog_name = ""

   override static func primaryKey() -> String? {
        return "dog_id"
    }
}

Ключ в том, что объект List содержит ссылки на хранящиеся в нем объекты. Он не «держит» эти реальные объекты. Если свойства объекта в списке изменяются, сам список не изменяется и все еще содержит ссылку на этот измененный объект.

Так, скажем, у нашего человека есть две собаки.

let person = PersonClass()

let dog0 = DogClass()
dog0.dog_name = "Spot"
let dog1 = DogClass()
dog1.dog_name = "Rover"

person.dogs.append(objectsIn: [dog0, dog1])

try! realm.write {
    realm.add(person)
}

, поэтому сейчас у человека две собаки; Spot and Rover.

Предположим, мы хотим изменить имя Rovers на Lass ie. Нет необходимости изменять или обновлять список людей - просто обновите свойство имени собаки в области, убедившись, что мы используем тот же dog_id, и это изменение будет отражено в списке людей.

let updatedDog = DogClass()
updatedDog.dog_id = "the dogs primary key"
updatedDog.dog_name = "Lassie"

try! realm.write {
   realm.add(updatedDog, update: .modified)
}
...