Ios SwiftUI核心数据无法从列表中删除多行

Ios SwiftUI核心数据无法从列表中删除多行,ios,core-data,swiftui,Ios,Core Data,Swiftui,我有一个SwiftUI应用程序,具有完整的SwiftUI流和核心数据持久性。这是一个基本的问题 主控/细节风格,并按预期稳定工作。我想添加一个功能以允许 要选择和删除的多行。单行滑动以按预期删除作品 我从中得到了灵感:58910010然而,该示例使用了 struct items和我无法使用核心数据对象复制过程 基本上,显示编辑复选框和按钮切换的功能 功能按预期工作,但当我点击垃圾箱图标时,应用程序崩溃。有 控制台中没有任何信息,线程日志中也没有任何帮助 请参见以下deleteCards()中的注

我有一个SwiftUI应用程序,具有完整的SwiftUI流和核心数据持久性。这是一个基本的问题 主控/细节风格,并按预期稳定工作。我想添加一个功能以允许 要选择和删除的多行。单行滑动以按预期删除作品

我从中得到了灵感:58910010然而,该示例使用了 struct items和我无法使用核心数据对象复制过程

基本上,显示编辑复选框和按钮切换的功能 功能按预期工作,但当我点击垃圾箱图标时,应用程序崩溃。有 控制台中没有任何信息,线程日志中也没有任何帮助

请参见以下deleteCards()中的注释1。如果我注释掉viewContext保存,则应用程序会这样做 不会崩溃,选定的卡将从列表中删除,但在重新启动时,这些卡将被删除 仍然存在。如果我将viewContext保存留在原位,应用程序将崩溃,但会继续运行 重新启动我看到所选项目不再存在

以下是一个简化版本:

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Card.lastName, ascending: true)],
        animation: .default)
    private var cards: FetchedResults<Card>
    @State var editMode: EditMode = .inactive
    @State var selection = Set<UUID>()

    var body: some View {
        NavigationView {
            List(selection: $selection) {
                ForEach(cards) { card in
                    VStack(alignment: .leading) {
                        Text("\(card.wrappedFirstName) \(card.wrappedLastName)")
                        Text("\(card.wrappedComment)")
                    }
                }
                .onDelete(perform: deleteItems)
            }
            .navigationBarItems(
                leading: editButton,
                trailing:
                    HStack {
                        addDelButton
                        addExamplesButton
                    }
            )
            .environment(\.editMode, self.$editMode)
            .navigationTitle("Cards")
        }//nav
    }//body

    private var editButton: some View {
        Button(action: {
            self.editMode.toggle()
            self.selection = Set<UUID>()
        }) {
            Text(self.editMode.title)
        }
    }//edit button

    private var addDelButton: some View {
        if editMode == .inactive {
            return Button(action: addCard) {
                Image(systemName: "plus")
            }
        } else {
            return Button(action: deleteCards) {
                Image(systemName: "trash")
            }
        }
    }//add del button

    private var addExamplesButton: some View {
        Button(action: {
            self.addExampleRecords()
        }) {
            Image(systemName: "gear")
        }
    }//add examples

    private func deleteCards() {
        for id in selection {
            if let index = cards.lastIndex(where: { $0.id == id }) {
                viewContext.delete(cards[index])
            }
        }
        selection = Set<UUID>()
    
        //Note 1: this crashed the app - but deletions happen
        do {
            try viewContext.save()
        } catch {
            // Replace this
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }//delete Cards

    private func addCard() {
        withAnimation {
            let newCard = Card(context: viewContext)
            newCard.id = UUID()
            newCard.firstName = "NewFirstName"
            newCard.lastName = "NewLastName"
            newCard.creationDate = Date()

            do {
                try viewContext.save()
            } catch {
                // Replace this
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }//add card

    private func addExampleRecords() {
    
        let context = self.viewContext
        for c in 0..<10 {
            let nmo = Card(context: context)
            nmo.id = UUID()
            nmo.lastName = "ExLast \(c)"
            nmo.firstName = "ExFirst \(c)"
            nmo.comment = "ExComment \(c)"
            nmo.creationDate = Date()
        }
    }//add example records

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { cards[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                // Replace this
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }//delete items
}//struct

extension EditMode {
    var title: String {
        self == .active ? "Done" : "Edit"
    }
    mutating func toggle() {
        self = self == .active ? .inactive : .active
    }
}// ext
struct ContentView:View{
@环境(\.managedObjectContext)私有变量viewContext
@获取请求(
SortDescriptor:[NSSortDescriptor(密钥路径:\Card.lastName,升序:true)],
动画:。默认设置)
私有var卡:获取结果
@状态变量editMode:editMode=.inactive
@State var selection=Set()
var body:一些观点{
导航视图{
列表(选择:$selection){
ForEach(cards){card in
VStack(对齐:。前导){
文本(\(card.wrappedFirstName)\(card.wrappedLastName))
文本(“\(card.wrappedComment)”)
}
}
.onDelete(执行:删除项)
}
.航海术语(
引导:编辑按钮,
尾随:
HStack{
addDelButton
addExamplesButton
}
)
.environment(\.editMode,self.$editMode)
.导航标题(“卡片”)
}//导航
}//身体
私有var编辑按钮:一些视图{
按钮(操作:{
self.editMode.toggle()
self.selection=Set()
}) {
文本(self.editMode.title)
}
}//编辑按钮
私有var addDelButton:一些视图{
如果editMode==.inactive{
返回按钮(操作:添加卡){
图像(系统名称:“plus”)
}
}否则{
返回按钮(操作:删除卡片){
图像(系统名称:“垃圾”)
}
}
}//添加删除按钮
私有变量addExamplesButton:一些视图{
按钮(操作:{
self.addExampleRecords()
}) {
图像(系统名称:“齿轮”)
}
}//添加示例
专用功能删除卡(){
用于选择中的id{
如果let index=cards.lastIndex(其中:{$0.id==id}){
viewContext.delete(卡片[索引])
}
}
选择=设置()
//注意1:这使应用程序崩溃-但会发生删除
做{
请尝试viewContext.save()
}抓住{
//替换这个
设nsError=错误为nsError
fatalError(“未解决的错误\(nsError),\(nsError.userInfo)”)
}
}//删除卡片
私人func addCard(){
动画片{
让newCard=Card(上下文:viewContext)
newCard.id=UUID()
newCard.firstName=“NewFirstName”
newCard.lastName=“NewLastName”
newCard.creationDate=日期()
做{
请尝试viewContext.save()
}抓住{
//替换这个
设nsError=错误为nsError
fatalError(“未解决的错误\(nsError),\(nsError.userInfo)”)
}
}
}//添加卡
私有函数addExampleRecords(){
让context=self.viewContext
对于0..NSFetchRequest中的c{
返回NSFetchRequest(entityName:“卡”)
}
@NSManaged公共变量id:UUID
@NSManaged public var firstName:字符串?
@NSManaged public var注释:字符串?
@NSManaged public var lastName:字符串?
@NSManaged public var creationDate:日期?
public var wrappedFirstName:String{
名字??“没有名字”
}
公共变量wrappedLastName:String{
姓氏??“没有姓氏”
}
public var wrappedComment:String{
评论??“无评论”
}
公共变量wrappedCreationDate:日期{
创建日期??日期()
}
}
分机卡:可识别{
}   

任何指导都将不胜感激。Xcode 12.3 iOS 14.2

我记得我已经看到过类似的报告,无法使用Xcode 12.1/iOS 14.1复制崩溃,因此这是一个新版本缺陷。您提醒我在另一个应用程序中发现的问题。当您有核心数据手动创建属性文件时,它将标记为optionals(核心数据类型,不是Swift)如果更改,有时会中断流程。请注意,上面我已从UUID中删除了?。纠正了这一点,应用程序不再崩溃,但所选项目不会被删除。乍一看,选择行似乎不会将该对象添加到选择集中。有什么想法吗?
extension Card {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Card> {
        return NSFetchRequest<Card>(entityName: "Card")
    }

    @NSManaged public var id: UUID
    @NSManaged public var firstName: String?
    @NSManaged public var comment: String?
    @NSManaged public var lastName: String?
    @NSManaged public var creationDate: Date?

    public var wrappedFirstName: String {
        firstName ?? "no first name"
    }

    public var wrappedLastName: String {
        lastName ?? "no last name"
    }

    public var wrappedComment: String {
        comment ?? "no comment"
    }

    public var wrappedCreationDate: Date {
        creationDate ?? Date()
    }

}

extension Card : Identifiable {

}