【问题标题】:Realm iOS - How to Update List?Realm iOS - 如何更新列表?
【发布时间】:2020-03-04 17:56:18
【问题描述】:

我在 iOS 上将 Codable 与 Realm 一起使用,并发现更新保存在 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 接收到已经保存在 DB 中的数据时,我需要更新它。据我了解,已实现primarykey

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

将更新具有相同主记录的现有记录。 具有类 Item 的对象的属性已更新,但类别的 List 未更新。

我尝试获取保存在数据库中的现有类别列表,并通过调用 savedItem.categories.removeAll() 改变了与 savedItem 关联的类别对象,并使用 savedItem.categories.append(objectsIn: itemFromAPI.categories) 添加了新类别 当我用

保存它时
realm?.add(Item, update: .modified)

领域崩溃抛出异常 - “RLMException 原因:尝试创建具有现有主键值的类型的对象”

所以我尝试使用

删除类别
realm?.delete(Category)

然后添加与项目关联的修改后的类别,这正在工作。

我已将类别嵌套到多个级别,并通过再次获取它来更新它们,删除它们并再次读取它们被证明是一种痛苦。这就是更新记录在领域中的工作方式吗?

我可以通过简单地将新对象分配给一个项目并使用

来更新与一个项目相关的嵌套类别吗?
realm?.add(Item, update: .modified) 

要像添加新项目那样更新记录?

任何回复将不胜感激。

【问题讨论】:

  • 我想你在这里问的是如何使领域列表属性可编码,对吧?如果是这样,那么这里已经多次询问和回答了。请参阅this question and answerthis question。检查这些,看看它们是否有帮助,因为 List 对象不能直接编码或解码
  • 没有。我的问题是如何使用 realm?.add(Item, update: .modified) 函数更新领域列表属性。它似乎没有更新 List 属性。我是否应该获取现有的数据库条目然后删除条目,使用相同的主键创建新条目以进行更新,或者我是否可以简单地构建包含新值列表的主对象并使用设置了更新标志的领域添加功能更新它们?
  • 我想我有一个答案。让我知道是否需要澄清。

标签: ios realm realm-list


【解决方案1】:

根据评论,问题是

我的问题是如何使用 realm?.add(Item, update: .modified) 函数。

为了清楚起见,让我重申一下这个问题

如何更新一个对象存储在另一个对象的List属性所在的位置 列表中存储的对象有一个主键

简短的回答是:List 对象包含对该列表中其他对象的引用。因为它包含引用,所以 List 本身不需要更新,只需更新它所引用的对象

长答案是通过一个示例:让我们以一个拥有 Dogs List 属性的 Person 为例

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 和 Rover。

假设我们要将 Rovers 名称更改为 Lassie。无需更改或更新人员列表 - 只需更新领域中的狗名称属性,确保我们使用相同的 dog_id,并且该更改将反映在人员列表中。

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

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

【讨论】:

  • 感谢 Jay 向我解释这一点。由于我在 Realm 中使用 Swift Codable,因此我的优势是可以将所有内容解析并合并到包含列表的父类对象中。我想知道我是否可以使用这种方法来更新父列表以及与父关联的子列表,就像我将新对象插入 Realm DB 一样。根据您的示例,当狗信息发生变化时,我会获得包含狗数据的完整人员记录。使用您的方法,我将不得不更新人员数据并获取相关的狗列表,并循环遍历单个狗来更新它。
  • @Prabhu 通常情况下,Codable 带来的麻烦多于其价值。一个很好的用例是,如果您将直接 JSON 导入 Swift 对象,但是 Realm 不是 Swift - 它由 ObjC 代码和对象支持,您可能会发现使用映射到对象属性的初始化程序代码创建对象会更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多