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
  }
}
Post not yet marked as solved Up vote post of tomato-paste Down vote post of tomato-paste
984 views
  • 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

  • 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.

Add a Comment

Replies

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

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
	}
}