如何在 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
,因为离开?你忽略了有一个问题。
作为一般规则,每个 ?和 !应该以if
、if let
或guard
开头。
现在来帮助你让它工作......
所有 CoreData 对象都是ObservableObject
s,因此如果您想查看更改/编辑它们,您必须将它们包装在@ObservedObject
中,您必须将TextField
s 之类的内容放在这样的子视图中。
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
而不是 @FetchRequest
或 NSFetchedResultsController
来观察存储,因此您必须在存储更改时以某种方式触发刷新。
一种方法是当你添加一个对象时,你可以调用你的获取方法。
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 帖子。它适用于任何Optional
String
,因此您必须创建临时包装变量。我希望我有它的原始链接,但我找不到它。太棒了。
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 中正确保存?的主要内容,如果未能解决你的问题,请参考以下文章