SwiftData does not cascade delete

Even if there are one-many relationship models with the cascade delete rule, SwiftData does not cascade delete items.

For example, there is one school has multiple students like the following models, and even when the school is deleted, the students in the school are not deleted. This happens when a user create a school and students and delete the school immediately.

Are there any workarounds for now?

@Model
final class School {
  var name: String

  @Relationship(deleteRule: .cascade, inverse: \Student.school)
  var students: [Student] = []

  init(name: String) {
    self.name = name
  }
}
@Model
final class Student {
  var fullName: String
  var school: School

  init(fullName: String, school: School) {
    self.fullName = fullName
    self.school = school
  }
}

Not working for me neighter, SwiftData do not cascade delete relationships.

I had to remove the inverse to get the cascade delete to work for me. I added it trying to use iCloud. However as iCloud was proving to be a nightmare I abandoned that route. So as I no longer need the requirement for the inverse. I removed it. Then the cascade delete for me worked

I also has the same issue.... did you got any solution??

Also not working for me..."child" objects are not being deleted when using the .cascade option. Could someone be abe to assist?

I'm using SwiftData with CloudKit enabled. Here's a simplified version of my model, where Score objects are not being deleted upon deletion of Match objects :

import SwiftData

@Model
class Match {
    // CloudKit integration requires all relationship properties to be optional
    @Relationship(deleteRule: .cascade, inverse:\Score.match) var score: Score?

    init() {
        self.score = Score(
            startServing: true,
            decidingSetType: .regular
        )
    }

}

@Model
class Score {
    // CloudKit integration requires all relationship properties to be optional
    var match: Match?
    // CloudKit integration requires all non optional properties to have a default value
    let startServing: Bool = true
    let decidingSetType: DecidingSetType = DecidingSetType.regular

    init(
        startServing: Bool,
        decidingSetType: DecidingSetType
    ) {
        self.startServing = startServing
        self.decidingSetType = decidingSetType
    }
}

enum DecidingSetType: Codable {
    case regular
    case superTiebreak
}

It seems I also have the same issue.. Hope this fixes. I also found a StackOverflow post mentioning the issue: https://stackoverflow.com/questions/77559646/swiftdata-cascade-deletion-rule-not-working

I have the same problem, but I've seen it working before:

https://youtu.be/dAMFgq4tDPM?si=JPFuB42gHN0sl7zK&t=1432

I found out that explicitly saving the modelContext prevents the cascade deleteRule to work. I filled a Feedback report : FB13640004.

I found a way to achieve deletion, saving context and make cascade deleteRule working. I'm not sure why it works but it does. This is particular useful for custom ModelContext where autoSave is disabled. First, I make sure that the autoSave feature is disabled. Next, I delete all the models in a single transaction which allows the deletion of child models by cascade. Then I rollback the context. At this step, child models are deleted but not parents. That's why I finally delete models in a new transaction.

I know that it's not ideal but It seems to work waiting for a fix.

This is my workaround :

extension ModelContext {

	/// Deletes models from ModelContext and saves changes to the persistent storage.
	/// - Parameter models: the models to delete.
	/// - Warning: This method is a workaround for deleting models and saving context since explicitly saving context prevents `cascade` delete rule to work.
	public func deleteAndSave<T: PersistentModel>(_ models: [T]) throws {
		func deleteModels() {
			for model in models {
				delete(model)
			}
		}

		let initialAutosaveState = autosaveEnabled

		autosaveEnabled = false

		try transaction {
			deleteModels()

			rollback()
		}

		try transaction {
			deleteModels()
		}

		autosaveEnabled = initialAutosaveState
	}
}

Same here, I have to remove the inverse in one-to-many case. Looks like it's a bug in SwiftData since CoreData's behavior confirms the necessity of the inverse.

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)
		
		//selected1st = new1st		<== This stops cascade deletion from working
	}

	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)
	}
}
SwiftData does not cascade delete
 
 
Q