【问题标题】:How to save correctly in Core Data?如何在 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 现已添加,检查。
  • 您的代码中发生了太多事情,以至于错误可能出现在任何地方。我会开始但摆脱那个绑定并直接用@ObservedObject包装CoreData对象。 wrappedDeliveryAddress 看起来也很可疑。
  • 如果我要摆脱那个绑定,我不能使用“.first”在 TextField 中绑定

标签: swift core-data swiftui


【解决方案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
        }
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-19
    • 2016-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多