如何从 SwiftUI 和 Realm 中另一个列表中的对象中添加和删除列表中的对象

Posted

技术标签:

【中文标题】如何从 SwiftUI 和 Realm 中另一个列表中的对象中添加和删除列表中的对象【英文标题】:How to add and delete objects from a List from an object who's inside another List in SwiftUI and Realm 【发布时间】:2021-08-16 13:44:33 【问题描述】:

在以下代码中,我有一个Cars 的List,并且该列表中的每个Car 都有自己的Services 列表,我可以通过调用添加和删除Cars 而不会出现问题carViewModel.addNewCar(make:String, model:String)carViewModel.deleteCar(at indexSet:IndexSet)

汽车.swift

    import RealmSwift
    final class Car: Object, ObjectKeyIdentifiable

        @objc dynamic var make: String = ""
        @objc dynamic var model: String = ""
        // creation date, ID etc.
        dynamic var services = List<CarService>()
    

CarList.swift

    import RealmSwift

    final class CarList: Object, ObjectKeyIdentifiable
        
        @objc dynamic var name: String = ""
        // creation date, ID etc.
        var cars = RealmSwift.List<Car>()
    

CarService.swift

    import RealmSwift

    final class CarService: Object, ObjectKeyIdentifiable

        @objc dynamic var serviceName: String = ""
        // creation date, ID etc.
    

查看模型

    import RealmSwift

    class CarViewModel: ObservableObject
        @Published var cars = List<Car>()
        @Published var selectedCarList: CarList? = nil

        var token: NotificationToken? = nil
        
        init()
            // Create a the default lists if they don't already exist.
            createDefaultCarList()
            createDefaultServiceList()
            
            // Initialize the SelectedCarList and the cars variables items from the Default Car List.
            if let list = realm?.objects(CarList.self).first
                self.selectedCarList = list
                self.cars = list.cars
            
            
            token = selectedCarList?.observe( [unowned self] (changes) in
                switch changes
                case .error(_): break
                case.change(_, _):self.objectWillChange.send()
                case.deleted: self.selectedCarList = nil
                
            )
        
        
        func addNewCar(make:String, model:String)
            if let realm = selectedCarList?.realm
                try? realm.write
                    let car = Car()
                    car.make = make
                    car.model = model
                    selectedCarList?.cars.append(car)
                
            
        
        
        func deleteCar(at indexSet:IndexSet)
            if let index = indexSet.first,
            let realm = cars[index].realm
                try? realm.write
                    realm.delete(cars[index])
                
            
        
        
        func addService(toCar: Car, serviceName: String)
            try? realm?.write
                let service = CarService()
                service.serviceName = serviceName
                
                toCar.services.append(service)
            
        
        
        /// Creates the Default Car List if it doesn't already exists otherwise just prints the error.
        func createDefaultCarList()
            do 
                if (realm?.objects(CarList.self).first) == nil
                    try realm?.write(
                        let defaultList = CarList()
                        defaultList.name = "Default Car List"
                        realm?.add(defaultList)
                    )
                
            catch let error
                print(error.localizedDescription)
            
        
        
        /// Creates the Default Serivice List if it doesn't already exists otherwise just prints the error.
        func createDefaultServiceList()
            do 
                if (realm?.objects(ServiceList.self).first) == nil
                    try realm?.write(
                        let defaultList = ServiceList()
                        defaultList.listName = "Default Service List"
                        realm?.add(defaultList)
                    )
                
            catch let error
                print(error.localizedDescription)
            
        
    

我的问题是将Services 添加或删除到现有的Cars。当我致电carViewModel.addService(toCar: Car, serviceName: String) 时,我收到以下错误...

调用 addService() 方法。

    struct NewServiceFormView: View 
        @ObservedObject var carViewModel: CarViewModel
        @State var selectedCar:Car // pass from other cars view
        
        var body: some View 
            NavigationView 
            Form 
                // fields
            
            .navigationBarItems( trailing:Button("Save", action: addNewCar))
            
        

        func addNewCar()
            carViewModel.addService(toCar: selectedCar, serviceName: "Oil Change")
        
    

错误

“无法在写入事务之外修改托管 RLMArray。”

我可以通过从cars 列表中明确选择Car 来添加新的Services。我没有收到任何错误,但 UI 没有更新;在应用重新启动之前,我看不到新添加的服务。

这样做没有错误,但 UI 不会更新。

carViewModel.addService(toCar: carViewModel.cars[1], serviceName: "Rotors")

如何正确查看、删除Services 并将其添加到现有Cars?

编辑:根据Mahan 的请求添加了以下代码。

View 呈现 NewServiceFormView

    struct CarServicesView: View 
        @State var selectedCar:Car // a car from parent view
        @ObservedObject var carViewModel: CarViewModel
        
        var body: some View 
            VStack

                List 
                    Section(header: Text("Services: \(selectedCar.services.count)")) 
                        ForEach(selectedCar.services)  service in
                        
                    
                
                .listStyle(GroupedListStyle())
                .toolbar 
                    ToolbarItem(placement: .navigationBarTrailing) 
                        Button(action: openNewServiceForm) 
                            Image(systemName: "plus")
                        
                    
                
            .sheet(isPresented: $newServiceFormIsPresented)
                NewServiceFormView(carViewModel: carViewModel, selectedCar: selectedCar)
            
        
        
        func openNewServiceForm() 
        newServiceFormIsPresented.toggle()
        
    

【问题讨论】:

能否将您使用过NewServiceFormView 的视图包含在其中? 在父视图中我刚刚提出了NewServiceFormView .sheet(isPresented: $newServiceFormIsPresented) NewServiceFormView(carViewModel: carViewModel, selectedCar: selectedCar) 谢谢。 @mahan - 查看更新的代码。谢谢。 【参考方案1】:

一个问题是如何观察 Realm 对象 - 请记住它们是底层的 ObjC 对象,因此您需要使用 Realm 观察者。所以这个

@ObservedObject var carViewModel: CarViewModel

应该是这样的

@ObservedRealmObject var carViewModel: CarViewModel

查看observedRealmObject的文档

另外,请记住,如果您正在观察结果,同样的事情也适用,使用

@ObservedResults

如documentation所示

【讨论】:

嗯,不幸的是,这并不像将@ObservedObject 替换为@ObservedRealmObject 那样简单。我目前正在使用 Swift/Combine @StateObjectObservedObject,所以我需要多玩一点,看看如何使用最近添加的 @ObservedResults@ObservedRealmObject 包装器。谢谢!

以上是关于如何从 SwiftUI 和 Realm 中另一个列表中的对象中添加和删除列表中的对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中使用 Realm

如何让 TextField 值更改触发 SwiftUI 中另一条数据的更新?

SwiftUI:将视图与 VStack 中另一个视图的前缘和后缘对齐

使用 Combine 和 SwiftUI 在 Realm 中观察收集结果

如何通过给定Linux中另一个文件中的列来从文件中删除列?

SwiftUI + Realm:从列表中删除一行会导致异常“RLMException”,原因:“对象已被删除或失效。”