Reputation: 100
I've tried all kinds of solutions proposed here and elsewhere (eg, adding extensions to Collection, [safe] element iteration, etc.), but nothing has worked unfortunately. I've also tried changing "last!" to "first!" in the onDelete code, but that causes the same issue although surprisingly it did work for others in the other threads I've seen. I should note that this only started to happen once I combined CoreData along with Firebase in my app... Your help would be tremendously appreciated. Thanks in advance.
Class used as an environment object and to initiate 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 struct used only for 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
}
Data model class used only for 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 {
}
Struct containing the code to my List and onDelete code:
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)
}
}
I have another view where I add new Data and save it to Firebase and CoreData like so:
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
}
}
Upvotes: 0
Views: 139
Reputation: 100
Ok so I finally figured it out after watching some great videos by kavsoft on youtube. For those that may run into similar issues, here's my solution:
Don't use the onDelete{} function on the ForEach. Instead create a button to delete items so you don't have to deal with indexes.
If working with CoreData & Firebase, create a string variable that will be equivalent to both the struct and core data class (see below).
In the Editing View (Add New view, etc.), I created the following:
@Binding var docID: String
Inside the list view, create an @State for the binding var for docID
@State var docID = ""
Then when I present the editing view from my list it reads as follows:
.sheet(isPresented: $show, content: {
EditView(docID: $docID, show: self.$show newEntry: true)
.environment(\.managedObjectContext, self.moc)
})
I added a Button that deletes the entry from Firebase, CoreData and the list simultaneously. This is nested inside the ForEach so you get a "minus" symbol next to each item in the list:
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. I also have a navigation bar button that triggers the "-" symbols to appear
var deleteButton : some View {
Button(action: {
self.remove.toggle()
}) {
Image(systemName: self.remove ? "xmark.circle" :
"trash")
}
Lastly, when saving the entry I set CoreData property:
let dayData = NSDayData(context: self.moc)
dayData.id = self.docID
Firebase property:
let db = Firestore.firestore()
db.collection("yourCollection").document(self.docID)
.setData(
[
"id": self.docID,
...
Upvotes: 1