After much time and effort I may be able to provide a clue for some people. My simple test app displays a List of parents - you select a parent, and a second List displays it's children. My first attempt worked fine - when I deleted a parent, it's children were cascade deleted (I checked the sqlite3 database to be sure).
Next I tried to programatically select a parent. Now, when I delete that parent, it's children are deleted in my app, but they continue to exist as orphans in the database.
Maybe someone brighter than me can figure out why...
| import SwiftUI |
| import SwiftData |
| |
| @main |
| struct Trial_SwiftDatabaseApp: App { |
| var sharedModelContainer: ModelContainer = { |
| let schema = Schema([FirstLevel.self, SecondLevel.self]) |
| let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) |
| |
| do { |
| return try ModelContainer(for: schema, configurations: [modelConfiguration]) |
| } catch { |
| fatalError("Could not create ModelContainer: \(error)") |
| } |
| }() |
| |
| var body: some Scene { |
| WindowGroup { |
| ContentView() |
| } |
| .modelContainer(sharedModelContainer) |
| } |
| } |
| |
| @Model |
| class FirstLevel { |
| var name: String |
| @Relationship(deleteRule: .cascade, inverse: \SecondLevel.my1st) var my2nds: [SecondLevel] |
| |
| init(name: String = "FirstLevel", my2nds: [SecondLevel] = []) { |
| self.name = name |
| self.my2nds = my2nds |
| } |
| } |
| |
| @Model |
| class SecondLevel { |
| var name: String |
| var my1st: FirstLevel |
| |
| init(name: String = "SecondLevel", my1st: FirstLevel) { |
| self.name = name |
| self.my1st = my1st |
| } |
| } |
| |
| struct ContentView: View { |
| @Environment(\.modelContext) private var modelContext |
| @Query private var firsts: [FirstLevel] |
| @Query private var seconds: [SecondLevel] |
| |
| @State private var selected1st: FirstLevel? = nil |
| @State private var selected2nd: SecondLevel? = nil |
| |
| var body: some View { |
| NavigationSplitView { |
| VStack { |
| List(firsts, id: \.self, selection: $selected1st) { first in |
| Text(first.name) |
| } |
| .onDeleteCommand() { |
| deleteFirst() |
| } |
| |
| Divider() |
| .background(.yellow) |
| |
| HStack { |
| Button { |
| addFirstLevel() |
| } label: { Text("+") } |
| |
| Spacer() |
| |
| Text("Firsts: \(firsts.count)") |
| } |
| .padding() |
| } |
| } detail: { |
| VStack { |
| List(filtered2nds, id: \.self, selection: $selected2nd) { second in |
| Text(second.name) |
| } |
| .onDeleteCommand() { |
| deleteSecond() |
| } |
| |
| Divider() |
| .background(.yellow) |
| |
| HStack { |
| Button { |
| addSecondLevel() |
| } label: { Text("+") } |
| |
| Spacer() |
| |
| Text("Seconds: \(seconds.count)") |
| } |
| .padding() |
| } |
| } |
| } |
| |
| var filtered2nds: [SecondLevel] { |
| guard let my1st = selected1st else { return [] } |
| let filtered = seconds.filter { $0.my1st == my1st } |
| return filtered |
| } |
| |
| func addFirstLevel() { |
| let new1st = FirstLevel(name: "New First") |
| modelContext.insert(new1st) |
| |
| |
| } |
| |
| func deleteFirst() { |
| guard let toDelete = selected1st else { return } |
| modelContext.delete(toDelete) |
| } |
| |
| func addSecondLevel() { |
| guard let my1st = selected1st else { return } |
| let new2nd = SecondLevel(name: "New Second", my1st: my1st) |
| modelContext.insert(new2nd) |
| } |
| |
| func deleteSecond() { |
| guard let my2nd = selected2nd else { return } |
| modelContext.delete(my2nd) |
| } |
| } |