致命错误:索引超出范围,同时在 SwiftUI 的列表中从 Firebase 中删除项目

Posted

技术标签:

【中文标题】致命错误:索引超出范围,同时在 SwiftUI 的列表中从 Firebase 中删除项目【英文标题】:Fatal error: Index out of range, while deleting item from Firebase inside a List in SwiftUI 【发布时间】:2020-12-10 12:34:35 【问题描述】:

我已经尝试过这里和其他地方提出的各种解决方案(例如,向 Collection 添加扩展、[安全] 元素迭代等),但遗憾的是没有任何效果。我也试过改变“最后!” “第一!”在 onDelete 代码中,但这会导致同样的问题,尽管令人惊讶的是它确实适用于我见过的其他线程中的其他人。我应该注意到,这只是在我将 CoreData 与 Firebase 结合到我的应用程序中之后才开始发生的......您的帮助将不胜感激。提前致谢。

用作环境对象并启动 Firebase 的类:

class DayData: ObservableObject, Identifiable  
    @Published var date = Date()
    @Published var completed = false
    @Published var percentage: Double = 0.0
    @Published var liquids: Double = 0
    
    var allData : [DayCellModel] = []
    
        let dbRef = Firestore.firestore()
        init() 
            readAllData()
        
        func readAllData()
            
            
            dbRef.collection("keanu").addSnapshotListener (snap, err) in

                guard let docs = snap else  return 

               
                    self.allData = docs.documents.compactMap( (doc) -> DayCellModel? in
                        return try! doc.data(as: DayCellModel.self)
                    )
             

DayCellModel 结构仅用于 Firebase:

struct DayCellModel : Identifiable, Codable 

    @DocumentID var id: String?
    var date : Date = Date()
    var completed : Bool = false
    var percentage : Double = 0.0
    var liquids: Int = 0

仅用于CoreData的数据模型类:

extension NSDayData 

    @nonobjc public class func getDayData() -> NSFetchRequest<NSDayData> 
        let request:NSFetchRequest<NSDayData> = NSDayData.fetchRequest() as! NSFetchRequest<NSDayData>
        
        let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
        request.sortDescriptors = [sortDescriptor]
        
        return request
    

    @NSManaged public var completed: Bool
    @NSManaged public var id: UUID?
    @NSManaged public var liquids: Double
    @NSManaged public var percentage: Double
    @NSManaged public var date: Date?



extension NSDayData : Identifiable 


包含代码到我的 List 和 onDelete 代码的结构:

    struct ListView: View 
        @Environment(\.managedObjectContext) var moc
        @FetchRequest(fetchRequest : NSDayData.getDayData()) var dayItems:FetchedResults<NSDayData>
        @EnvironmentObject var data : DayData
        
    
        var body: some View 
           NavigationView
    
                        VStack(spacing: 10)
                        HStack
                                    Text("Date")
                                    Text("Completion")
                                    Text("Liquids")
                                      
                                .font(.caption)
                            
                            List
                              ForEach(dayItems, id: \.id)  collection in
                                HStack
    
                                        Text("\(collection.date?.string(format: self.dateFormat) ?? Date().string(format: self.dateFormat))")
                             
    
                                        Text(collection.percentage > 0.95 ? "100%" : "\(Int(collection.percentage * 100))")
    
                                   
                                        Text("\(Int(collection.liquids))oz")
                                
                                    
                                .font(.system(size: 10, weight: .light))
                          
     
                              .onDelete (index) in
                                    let db = Firestore.firestore()
                                
                                    db.collection("keanu")
                                        .document(self.data.allData[index.last!].id!)
                                        .delete  (err) in
    
                                        if err != nil
    
                                            print((err?.localizedDescription)!)
                                            return
                                        
    
                                    
                                    self.deleteItems(at: index)
                              
                        
                    
func deleteItems(at offsets: IndexSet) 
        withAnimation
            offsets.map  dayItems[$0] .forEach(moc.delete)
            saveMoc()
            data.allData.remove(atOffsets: offsets)
        
    

我有另一个视图,我在其中添加新数据并将其保存到 Firebase 和 CoreData,如下所示:

if self.newCollection 
                let dayData = NSDayData(context: self.moc)
                dayData.id = UUID()
                dayData.date = self.data.date
                dayData.completed = self.data.percentage > 0.95
                dayData.percentage = self.data.percentage
                dayData.liquids = self.data.liquids

                do 
                    try moc.save()
                 catch 
                    let error = error as NSError
                    fatalError("Unresolved Error: \(error)")
                
                
                let db = Firestore.firestore()
                db.collection("keanu").document()
                    .setData(
                        [
                        "date": self.data.date,
                         "completed": self.data.percentage > 0.95,
                         "percentage": self.data.percentage,
                         "liquids": self.data.liquids
                        ])  (err) in
                        
                        if err != nil
                            
                            print((err?.localizedDescription)!)
                            return
                        
                
                    

【问题讨论】:

【参考方案1】:

好吧,在 youtube 上观看了 kavsoft 的一些精彩视频后,我终于明白了。对于那些可能遇到类似问题的人,这是我的解决方案:

    不要在 ForEach 上使用 onDelete 函数。而是创建一个按钮来删除项目,这样您就不必处理索引。

    如果使用 CoreData 和 Firebase,请创建一个字符串变量,该变量将等效于结构和核心数据类(见下文)。

    在编辑视图(添加新视图等)中,我创建了以下内容:

@Binding var docID: 字符串

    在列表视图中,为 docID 的绑定变量创建一个 @State

    @State var docID = ""

然后,当我从列表中显示编辑视图时,它的内容如下:

   .sheet(isPresented: $show, content: 
                EditView(docID: $docID, show: self.$show newEntry: true)
                 
                        .environment(\.managedObjectContext, self.moc)
                 )

    我添加了一个按钮,可以同时从 Firebase、CoreData 和列表中删除条目。它嵌套在 ForEach 中,因此您会在列表中的每个项目旁边获得一个“减号”符号:

    if self.remove
    
         VStack
              Button(action: 
                 let db = Firestore.firestore()
    
    
     db.collection("myCollection").document(docID).delete()
    
     moc.delete(collection)
    
     saveMoc()
      
    
    
     ) 
    
           Image(systemName: "minus.circle.fill")
    
          .buttonStyle(BorderlessButtonStyle())
       
    

5.2。我还有一个导航栏按钮,触发“-”符号出现

var deleteButton : some View 
        Button(action: 
            
            self.remove.toggle()
            
        ) 
           
            Image(systemName: self.remove ? "xmark.circle" : 
    "trash")
                
        

                

    最后,保存我设置的条目时 CoreData 属性:

        let dayData = NSDayData(context: self.moc)
         dayData.id = self.docID
    

Firebase 属性:

let db = Firestore.firestore()
                db.collection("yourCollection").document(self.docID)
                    .setData(
                        [
                         "id": self.docID,
...

【讨论】:

以上是关于致命错误:索引超出范围,同时在 SwiftUI 的列表中从 Firebase 中删除项目的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI .onDelete 抛出致命错误:索引超出范围

SwiftUI - 致命错误:从数组中删除元素时索引超出范围

索引超出范围的 SwiftUI 问题

SwiftUI:从 ForEach 中删除项目导致索引超出范围

准备转场时致命错误索引超出范围

Swift 致命错误:数组索引超出范围