首页 > 解决方案 > CoreData 和 SwiftUI:保存 NSManagedObjectContext 时出错

问题描述

编辑:我得到的错误是nilError代码0

编辑:这是一个简单的、最低限度可行的应用程序,它演示了这个问题:

@main
struct CoreDataTestApp: App {
    let persistantContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores { _, error in
            if let error = error {
                fatalError(error.localizedDescription)
            }
        }
        
        return container
    }()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistantContainer.viewContext)
        }
    }
}

struct ModalView: View {
    @Binding var isPresented: Bool
    @Environment(\.managedObjectContext) var managedObjectContext
    
    var body: some View {
        Button("Add Event") {
            let event = Event(context: managedObjectContext)
            
            event.name = "Hello"
            event.startDate = Date()
            event.endDate = Date().addingTimeInterval(86400)
            event.colorIndex = 0
            event.id = UUID()
            
            try! managedObjectContext.save()
            
            self.isPresented = false
        }
    }
}

struct ContentView: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    
    @FetchRequest(
        entity: Event.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Event.endDate, ascending: true)
        ]
    ) var events: FetchedResults<Event>
    
    @State var isPresented: Bool = false

    var body: some View {
        NavigationView {
            List {
                ForEach(events, id: \.id) { event in
                    Text(event.name!)
                }
            }
            .navigationTitle("List")
            .navigationBarItems(trailing: Button("Modal") {
                self.isPresented = true
            })
        }
        .sheet(isPresented: $isPresented) {
            ModalView(isPresented: $isPresented)
        }
    }
}

我正在 SwiftUI 2.0 中构建一个简单的倒计时时钟应用程序。最初,我将数据存储在 UserDefaults 中,但现在我正尝试将我的模型迁移到以前没有使用过的 CoreData。当我尝试保存时NSManagedObjectContext,我收到一条错误消息The operation couldn't be completed,并且我的模型永远不会更新,视图也不会更新,我不确定这是为什么。

这是我的主要应用程序结构:

@main
struct HourglassApp: App         
    let container: NSPersistentContainer = {
        let pc = NSPersistentContainer(name: "Model")
        pc.loadPersistentStores { _, error in
            if let error = error {
                fatalError(error.localizedDescription)
            }
        }
        return pc
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, container.viewContext)
        }
    }
}

这是我的 AddEventView:

struct AddEventView: View {
    @State var eventName: String = ""
    @State var endDate = Date().addingTimeInterval(60)
    @State var isAddedToCalendar = true
    @State var gradientIndex: Int = 0
        
    @Binding var showModal: Bool
    
    @Environment(\.managedObjectContext) var context: NSManagedObjectContext
        
    let existingEvent: Event?
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    HStack {
                        Text("Event Name")
                        
                        TextField("New Year's Day", text: $eventName)
                            .multilineTextAlignment(.trailing)
                    }
                    
                    DatePicker(
                        "Event Date     ",
                        selection: $endDate,
                        in: Date()...,
                        displayedComponents: [.date, .hourAndMinute]
                    )
                }
                
                Section {
                    ColorChooser(linearGradients, selectedIndex: $gradientIndex).padding(.vertical, 8)
                    
                    Toggle("Add to Calendar", isOn: $isAddedToCalendar)
                        .toggleStyle(SwitchToggleStyle(tint: accentColor))
                }
            }
            .navigationBarItems(
                leading: Button(action: {
                    self.showModal.toggle()
                }) {
                    Text("Cancel").foregroundColor(accentColor)
                }, trailing: Button(action: {
                    let adjustedEnd = Calendar.current.date(bySetting: .second, value: 0, of: endDate)!
                                        
                    DataProvider.shared.removeEvent(existingEvent, from: context)
                    
                    DataProvider.shared.addEvent(
                        in: context,
                        name: eventName,
                        range: (existingEvent?.startDate ?? Date())...adjustedEnd,
                        colorIndex: gradientIndex,
                        shouldAddToCalendar: isAddedToCalendar
                    )
                    
                    self.showModal.toggle()
                }) {
                    Text("Add").bold().foregroundColor(accentColor)
                }
                .onAppear {
                    if let event = existingEvent {
                        self.eventName = event.name!
                        self.endDate = event.endDate!
                        self.gradientIndex = Int(event.colorIndex)
                    }
                }
        }
    }
    
}

这是我的 DataProvider 类:

class DataProvider {
    static let shared = DataProvider()
    
    static func allEventsFetchRequest() -> NSFetchRequest<Event> {
        let request: NSFetchRequest<Event> = Event.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(key: Defaults.shared.comparatorKey, ascending: true)]
        return request
    }
    
    func addEvent(in context: NSManagedObjectContext, name: String, range: ClosedRange<Date>, colorIndex: Int, shouldAddToCalendar inCalendar: Bool) {
        let event = Event(context: context)
        event.name = name
        event.startDate = range.lowerBound
        event.endDate = range.upperBound
        event.colorIndex = Int16(colorIndex)
        event.id = UUID()
        
        UserNotifications.shared.registerEventNotification(event)
        if inCalendar {
            UserCalendar.shared.addEvent(event) { _ in }
        }
        
        do {
            try context.save()
        } catch {
            fatalError(error.localizedDescription)
        }
    }
    
    func removeEvent(_ event: Event?, from context: NSManagedObjectContext) {
        guard let event = event else { return }
        
        context.delete(event)
        
        UserNotifications.shared.unregisterEventNotification(event)
        UserCalendar.shared.removeEvent(event) { _ in }
        
        do {
            try context.save()
        } catch {
            fatalError(error.localizedDescription)
        }
    }
}

同样,在我显示所有事件的主 ContentView 中,我有

@FetchRequest(
    fetchRequest: DataProvider.allEventsFetchRequest()
) var events: FetchedResults<Event>

@Environment(\.managedObjectContext) var context: NSManagedObjectContext

我尝试使用类似的习语开发一个最小可行的 CoreData 示例应用程序,并且效果很好,所以我不确定这里有什么问题。

任何帮助将不胜感激,谢谢!

标签: iosswiftcore-dataswiftui

解决方案


推荐阅读