如何在 Core Data 中正确保存?

Posted

技术标签:

【中文标题】如何在 Core Data 中正确保存?【英文标题】:How to save correctly in Core Data? 【发布时间】:2022-01-19 02:52:00 【问题描述】:

我正在尝试将文本字段中的内容保存到 Core Data,但没有任何反应。什么都没有保存到核心数据中,不知道为什么,因为之前工作过。

struct LivrareView: View 
    @StateObject var coreDataViewModel = CoreDataViewModel()
    
   
    var body: some View 
        
   let delivery = Binding(
            get: coreDataViewModel.savedDetails.first?.wrappedDeliveryAddress ?? "",
            set: coreDataViewModel.savedDetails.first?.wrappedDeliveryAddress = $0)

VStack(alignment: .leading) 
                Text(Texts.livrareViewText1)
                    .foregroundColor(.orange)
         
                TextField("Ex", text: delivery , onEditingChanged:  _ in
                    coreDataViewModel.saveContext()
                )




    func saveContext() 
        DispatchQueue.main.async 
            do 
                try context.save()
                print("Savedd succesfully")
             catch let error 
                print("Error is \(error.localizedDescription)")
            
            self.fetchSavedMenu()
            self.fetchSavedDetails()
        
        

CoreData 视图模型:

class CoreDataViewModel : ObservableObject 
    let manager = CoreDataManager.instance
    @Published var savedMenu: [LocalMenu] = []
    @Published var savedDetails : [LocalDetails] = []
    
    var countDictionary: [Int16:Int] 
        savedMenu.reduce(into: [:]) 
          $0[$1.id, default: 0] += 1
      
    
    
    var savedCartToShow: [LocalMenu] 
        var alreadyThere = Set<Int16>()
        return savedMenu.compactMap  cart -> LocalMenu? in
            guard !alreadyThere.contains(cart.id) else  return nil 
            alreadyThere.insert(cart.id)
            return cart
        
    
    
    init() 
        
        fetchSavedMenu()
        fetchSavedDetails()
    
    func fetchSavedMenu() 
        
        let request = NSFetchRequest<LocalMenu>(entityName: "LocalMenu")
        do 
            savedMenu = try manager.context.fetch(request)
         catch let error 
            print("Error fetching \(error)")
        
    
    
    func fetchSavedDetails() 
        let request = NSFetchRequest<LocalDetails>(entityName: "LocalDetails")
        do 
            savedDetails = try manager.context.fetch(request)
         catch let error 
            print("Error fetching \(error)")
        
    
    
    func saveContext() 
        DispatchQueue.main.async 
            do 
                try context.save()
                print("Savedd succesfully")
             catch let error 
                print("Error is \(error.localizedDescription)")
            
            self.fetchSavedMenu()
            self.fetchSavedDetails()
        
        
    public func addTask(name: String, grams: Double, price: Float, id: Int) 
       
       let newCart = LocalMenu(context: manager.context)
        newCart.name = name
        newCart.grams = grams
        newCart.price = Int16(price)
        newCart.id = Int16(id)
        saveContext()
    

已编辑:添加了 CoreDataViewModel。 我有 2 个实体:LocalDetails 和 LocalMenus。 我有一个“食物菜单”列表,右侧有一个 + 号,当按下按钮时,会调用“addTask()”函数。

LocalDetails 用于为用户存储数据,但可能错误是没有为 LocalDetails 创建对象?

CoreData 管理器:

class CoreDataManager 
    
    static let instance = CoreDataManager() 
    
    let container : NSPersistentContainer
    let context : NSManagedObjectContext
    init() 
        container = NSPersistentContainer(name: "OneBiteContainer")
        container.loadPersistentStores  description, error in
            if let error = error 
                print("error loading core ddata \(error)")
            
        
        print(container.persistentStoreDescriptions.first?.url)
        context = container.viewContext
    
    func save()
    
        do 
            try context.save()
            print("Savedd succesfully")
         catch let error 
            print("Error is \(error.localizedDescription)")
        
    

【问题讨论】:

欢迎来到 Stack Overflow!请拨打tour 并查看:How do I ask a good question? 和How to create a Minimal, Reproducible Example。我们真的需要你 CoreDataViewModel 和你的 PersistenceController 来调试它,虽然我不确定你在哪里显示了你试图保存的托管对象。 @Yrb 现已添加,检查。 这能回答你的问题吗? ForEach not properly updating with dynamic content SwiftUI 您的代码中发生了太多事情,以至于错误可能出现在任何地方。我会开始但摆脱那个绑定并直接用@ObservedObject包装CoreData对象。 wrappedDeliveryAddress 看起来也很可疑。 如果我要摆脱那个绑定,我不能使用“.first”在 TextField 中绑定 【参考方案1】:

如果你改变 ?到一个!你会遇到崩溃,你会立即知道你的问题是自定义的Binding,因为离开?你忽略了有一个问题。

作为一般规则,每个 ?和 !应该以ifif letguard 开头。

现在来帮助你让它工作......

所有 CoreData 对象都是ObservableObjects,因此如果您想查看更改/编辑它们,您必须将它们包装在@ObservedObject 中,您必须将TextFields 之类的内容放在这样的子视图中。

struct DetailsView: View
    @ObservedObject var vm: CoreDataViewModel
    @ObservedObject var details: LocalDetails
    
    var body: some View
        TextField("Ex", text: $details.deliveryAddress.bound , onEditingChanged:  _ in
            vm.saveContext()
        )
    

你会像这样在LivrareView 中使用它。

struct LivrareView: View 
    @StateObject var coreDataViewModel = CoreDataViewModel()
    var body: some View 
        
        VStack(alignment: .leading) 
            Text("Texts.livrareViewText1")
                .foregroundColor(.orange)
            //Checking for `nil`
            if coreDataViewModel.savedDetails.first != nil 
                DetailsView(vm: coreDataViewModel, details: coreDataViewModel.savedDetails.first!)
            else //And reacting somehow to finding it
                Button("add details", action: 
                    coreDataViewModel.addDetail(address: "")
                )
            
            
        
    

现在您已选择不使用 NSFetchRequest 而不是 @FetchRequestNSFetchedResultsController 来观察存储,因此您必须在存储更改时以某种方式触发刷新。

一种方法是当你添加一个对象时,你可以调用你的获取方法。

class CoreDataViewModel : ObservableObject 
    let manager = CoreDataManager.instance
    @Published var savedMenu: [LocalMenu] = []
    @Published var savedDetails : [LocalDetails] = []
    
    var countDictionary: [Int16:Int] 
        savedMenu.reduce(into: [:]) 
            $0[$1.id, default: 0] += 1
        
    
    
    var savedCartToShow: [LocalMenu] 
        var alreadyThere = Set<Int16>()
        return savedMenu.compactMap  cart -> LocalMenu? in
            guard !alreadyThere.contains(cart.id) else  return nil 
            alreadyThere.insert(cart.id)
            return cart
        
    
    
    init() 
        
        fetchSavedMenu()
        fetchSavedDetails()
    
    func fetchSavedMenu() 
        
        let request = NSFetchRequest<LocalMenu>(entityName: "LocalMenu")
        do 
            savedMenu = try manager.context.fetch(request)
         catch let error 
            print("Error fetching \(error)")
        
    
    
    func fetchSavedDetails() 
        let request = NSFetchRequest<LocalDetails>(entityName: "LocalDetails")
        do 
            savedDetails = try manager.context.fetch(request)
         catch let error 
            print("Error fetching \(error)")
        
    
    
    func saveContext() 
        //Simplify this call the manager vs recreate code
        manager.save()
    
    public func addMenu(name: String, grams: Double, price: Float, id: Int) 
        
        let newCart = LocalMenu(context: manager.context)
        newCart.name = name
        newCart.grams = grams
        newCart.price = Int16(price)
        newCart.id = Int16(id)
        saveContext()
        
        fetchSavedMenu()
    
    
    public func addDetail(address: String) 
        
        let newCart = LocalDetails(context: manager.context)
        newCart.deliveryAddress = address
        saveContext()
        
        fetchSavedDetails()
    

另外,你会看到我使用.bound 来处理可选的。该代码来自另一个 SO 帖子。它适用于任何OptionalString,因此您必须创建临时包装变量。我希望我有它的原始链接,但我找不到它。太棒了。

extension Optional where Wrapped == String 
    var _bound: String? 
        get 
            return self
        
        set 
            self = newValue
        
    
    var bound: String 
        get 
            return _bound ?? ""
        
        set 
            _bound = newValue
        
    


【讨论】:

以上是关于如何在 Core Data 中正确保存?的主要内容,如果未能解决你的问题,请参考以下文章

如何在核心数据中正确保存可转换对象?

网页无法在谷歌浏览器中正确保存

如何在Java中正确定义多维通用集合?

在 Symfony 中正确保存相关实体(外键)

无法在数据库中正确保存不同语言的地址

如何在iOS7中正确定位后退按钮