I am fairly new to the iOS programming world and definitely new to SwiftData, so hopefully this isn't too much of a stupid question...
My SwiftData Schema consists of a one to many to many model. A project can have many reports, and each report can have many items. Project reports are unique to said project, and report items are unique to said report.
Where my issue is, I am trying to pass a list of reports of a specific project into a for each view in a list and then using the .onDelete method to call a function to delete a report out of the list. The problem is that quite often one of the reports I try to delete ends up 'coming back' whenever I navigate out of the view and back in, close the app, etc. The odd thing is that it doesn't happen every time, and it only happens when I delete 2 or more Reports.
Here is my code to (attempt to) do such that:
import SwiftUI
import SwiftData
struct ActiveReports: View {
@Environment(\.modelContext) private var context
@State private var alertSaveError = false
let projectReports: [Report]
var body: some View {
Group {
List {
ForEach(projectReports) { report in
NavigationLink {
ReportListView(report: report)
} label: {
HStack{
Text("\(report.scope)")
Text("Version: \(report.version)")
Spacer()
Text("\(report.dateCreated.formatted(date: .abbreviated, time:
.omitted))")
}
}
}
.onDelete(perform: deleteReport)
}
}
.alert("Failed to Save", isPresented: $alertSaveError ){
Button("OK", role: .cancel) { }
}
}
func deleteReport(_ indexSet: IndexSet) {
for item in indexSet {
let report = projectReports[item]
context.delete(report)
do{
try context.save()
} catch {
alertSaveError = true
print("Error in saving")
}
}
}
}
My Project model:
import Foundation
import SwiftData
@Model
class Project {
var name: String = ""
var projectNumber: String = ""
var scopes: [String] = [""]
init(name: String, projectNumber: String, scopes: [String]) {
self.name = name
self.projectNumber = projectNumber
self.scopes = scopes
}
@Relationship(deleteRule: .cascade)
var reports: [Report]? = []
}
and my Report model:
import Foundation
import SwiftData
@Model
class Report {
var version: Int = 1
var scope: String = ""
var dateCreated: Date = Date.now
var issued: Bool = false
init(version: Int, scope: String, issued: Bool) {
self.version = version
self.scope = scope
self.issued = issued
}
// Setting the relationship back to parent Project
@Relationship(inverse: \Project.reports)
var project: Project?
//Setting up the related punch items
@Relationship(deleteRule: .cascade)
var punchItems: [PunchItem]?
}
Now I have messed with this a bit and one thing I tried was using the @Query macro to bring in my list of reports instead of passing them into the view. That did work for deleting the reports reliably! The problem is that ends up showing all the reports for all 'Projects' which I don't want. I tried filtering the Query, but then I ran into the issue of not being able to dynamically filter Query results. (Passing in the project name so that I only receive its Reports). Maybe this is the route I should take here, and my knowledge is limiting me.
I also tried forcing a save on the model container just in case SwiftData wasn't automatically committing changes correctly. I never got a failure, but still the issue persisted. I saw that other posts state that similar behavior was a known bug in a previous version (in theory patched) and forcing a save was a work around. This does not seem to fix it for me.
All this makes me think that this is most certainly a logic failure on my end, but for the life of me I cannot figure out why the delete behavior works reliably with @Query macro implemented, vs ~50% of the time when passing the array of items into the view.
EDIT: I am not quite sure why this is the case, but I switched my .onDelete method to a .swipeActions on the items within the ForEach loop and the delete behavior works correctly now. Bug? Maybe, but more likely a lack of understanding on my end.