【问题标题】:How to completely delete items when using @ObservedResults in Realm and SwiftUI在 Realm 和 SwiftUI 中使用 @ObservedResults 时如何完全删除项目
【发布时间】:2021-08-19 14:40:12
【问题描述】:

我正在使用Realm 文档Integration Guides -> SwiftUI & Combine 中给出的示例尝试新的Realm 10 包装器,我喜欢使用@ObservedResults@ObservedRealmObject 时添加和删除记录是多么简单.我不太明白的一件事是为什么在从Group 对象中删除项目时,它只会从Group 中删除项目,但不会删除Item 领域对象中的实际项目。请参阅下面的领域浏览器图像。

这是通过应用程序 UI 添加和删除四 (4) 个项目后 Realm Browser 显示的内容,您可以看到这四 (4) 个项目已从 Group 中删除,但所有四 (4) 个项目都保留在Item 对象。

有人可以解释为什么在调用.onDelete(perform: $group.items.remove) 时,项目不会从Item 对象中删除,而只是从Group 对象中删除?我怎样才能删除它们?

我试着像这样删除它们...

ItemsView.swift

.onDelete(perform: deleteItems)


func deleteItems(at offsets: IndexSet){
    let realm = try? Realm()
    
    try! realm?.write {
        // 1. delete items
        for item in list.items{
            realm?.delete(item)
        }
        // 2. delete the list
        realm?.delete(list)
    }
}

但我收到以下错误:

线程 1:“只能从它所属的 Realm 中删除一个对象。”

同样,整个代码可以在Integration Guides - 不同步。

编辑:为 LocalOnlyContentView、ItemsView、Group 和 Item 模型添加了代码。

Item.swift

    import Foundation
    import RealmSwift

    /// Random adjectives for more interesting demo item names
    let randomAdjectives = [
        "fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",
        "acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",
        "aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"
    ]
    /// Random noun for more interesting demo item names
    let randomNouns = [
        "floor", "monitor", "hair tie", "puddle", "hair brush", "bread",
        "cinder block", "glass", "ring", "twister", "coasters", "fridge",
        "toe ring", "bracelet", "cabinet", "nail file", "plate", "lace",
        "cork", "mouse pad"
    ]

    final class Item: Object, ObjectKeyIdentifiable {
        @Persisted(primaryKey: true) var _id: ObjectId
        @Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"
        @Persisted var isFavorite = false
        @Persisted(originProperty: "items") var group: LinkingObjects<Group>
    }

Group.swift

import Foundation
import RealmSwift


final class Group: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var items = RealmSwift.List<Item>()
}

ItemsView.swift

struct ItemsView: View {
    @ObservedRealmObject var group: Group

    var leadingBarButton: AnyView?
    
    var body: some View {
        NavigationView {
            VStack {
                // The list shows the items in the realm.
                List {
                    ForEach(group.items) { item in
                        ItemRow(item: item)
                    }
                    .onDelete(perform: $group.items.remove)
                    .onMove(perform: $group.items.move)
                }.listStyle(GroupedListStyle())
                    .navigationBarTitle("Items", displayMode: .large)
                    .navigationBarBackButtonHidden(true)
                    .navigationBarItems(
                        leading: self.leadingBarButton,
                        trailing: EditButton())
                HStack {
                    Spacer()
                    Button(action: {
                        $group.items.append(Item())
                    }) { Image(systemName: "plus") }
                }.padding()
            }
        }
    }
}

LocalOnlyContentView.swift

struct LocalOnlyContentView: View {
    @ObservedResults(Group.self) var groups
    
    var body: some View {
        if let group = groups.first {
            AnyView(ItemsView(group: group))
        } else {
            AnyView(ProgressView().onAppear {
                $groups.append(Group())
            })
        }
    }
}

SceneDelegate.swift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let contentView = LocalOnlyContentView()
    
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}

【问题讨论】:

  • 我认为您的模型可能已关闭 - 如果您尝试匹配指南,Item 对象应该具有 group 属性。你能包括你的组和项目模型吗?
  • 查看更新的代码。我还为 SceneDelegate、LocalOnlyContentView 和 ItemsView 添加了代码。请注意,我直接从我的项目中复制并粘贴了代码。谢谢杰!
  • RealmStudio 屏幕截图中的内容似乎与这些模型不匹配。 Item 类有一个 isFavorite 属性,它不在屏幕截图中的 Item 模型中。
  • 糟糕——你是对的,我忽略了它。因此,在该镜头中,您有一个 Group,其包含不包含任何项目的项目的 List 属性。例如List 是指向项目的“指针”,因此如果您从 List 中删除项目,它仍然存在于 Realm 中,只是不在 List 中。如果您删除实际项目,它也会将其从列表中删除。所以这个group.items.remove 会将它从列表中删除,而不是从领域中删除。你问的是这个吗?
  • 对,正如我所提到的,从列表中删除项目,只是从列表中删除它们,它不会删除实际的项目。如果要删除实际项目,则需要从 Realm 中删除 item,而不是从列表中删除。

标签: swiftui realm


【解决方案1】:

请记住,该教程中显示的是如何从组的列表中删除一个项目,而不是如何完全删除该项目。

这个答案的水平很高 - List 中的对象不是实际的对象 - 它们是指向存储在磁盘上的实际项目的“指针”。

假设你有三个项目

Item 0 
Item 1
Item 2

和一个包含项目列表的组

MyGroup
   List of items
        Item 0
        Item 1
        Item 2

实际发生的事情是列表“指向”磁盘上的项目

My Group
   List of items
      pointer to Item 0
      pointer to Item 1
      pointer to Item 2

所以当它被称为$group.items.remove 时,它会从列表中删除指向该项的指针,而不是该项本身。

解决方案(嗯,解决方案之一)是删除实际项目

let myItemToRemove = List of items[0] //get the item at index 0

realm.remove(myItemToRemove) // removes the item itself from realm, along 
                             //    with the pointer stored in the list

实际删除项目的代码是这样的

try? realm.write {
   realm.delete(objectToDelete)
}

【讨论】:

  • 非常感谢您对列表如何工作的很好的解释,这很有帮助。至于删除领域对象,我已经尝试通过迭代列表中的所有项目来做到这一点,但我得到了一个错误。从我的原始帖子中查看我的第一段代码。谢谢。
  • @fs_tigre 是的 - 我明白了。如果您在指南中注意到此self.realm = realm,这是最初写入的对象。稍后,这在您的代码let realm = try? Realm() 中有所不同。尝试使用写入数据的相同领域变量。
  • 我正在使用没有同步的示例,它没有self.realm = realm。我会再深入研究一下,看看我能不能让它发挥作用。谢谢。
  • @fs_tigre 我很乐意以它为例。问题是观察到的对象领域和删除语句中的领域是不同的。您应该能够从let realm = group.realm 中的组中获取正确的领域,然后使用该领域删除组对象。我面前没有确切的代码,但这就是我的想法。
  • 知道了,我会告诉你情况如何。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-19
  • 2021-05-13
  • 2017-10-05
  • 2011-10-04
  • 1970-01-01
相关资源
最近更新 更多