I’m having an issue when two of my SwiftData models have a one-to-many relationship and I have them synced via CloudKit.
To be clear, I’ve met all of the requirements to make it iCloud friendly and sync to work. I followed this https://www.hackingwithswift.com/quick-start/swiftdata/how-to-sync-swiftdata-with-icloud, and can confirm I’ve done it correctly because initially I was seeing this crash on startup when I had not:
Thread 1: Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer)
This is to say, the problem may be iCloud related but it’s not due to a wrong model setup. Speaking of which, these are models:
@Model
class Film {
var name: String = ""
var releaseYear: Int = 0
var director: Director? = nil
init(name: String, releaseYear: Int, director: Director) {
self.name = name
self.releaseYear = releaseYear
self.director = director
}
}
@Model
class Director {
var name: String = ""
@Relationship(deleteRule: .cascade, inverse: \Film.director)
var films: [Film]? = []
init(name: String, films: [Film]) {
self.name = name
self.films = films
}
}
I’ve set the delete rule for the relationship between Film
and Director
to be cascading because you can’t have a film without a director (to be clear, even when set as nullify, it doesn’t make a difference)
And this is the @main App
definition:
@main
struct mvpApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Film.self,
Director.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()
}
}
And this is the dummy ContentView
:
struct ContentView: View {
var body: some View {
EmptyView()
.onAppear {
let newDirector = Director(name: "Martin Scorcese", films: [])
let film = Film(name: "The Wolf of Wall Street", releaseYear: 2019, director: newDirector)
newDirector.films!.append(film)
}
}
}
I create a Director
with no films assigned. I then create a Film
, and the append it to the Director
’s [Film]
collection.
The last step however causes a crash consistently:
There is a workaround that involves removing this line from the Film
init()
:
self.director = director // comment this out so it’s not set in a Film’s init()
When I do this, I can append the (Director
-less) Film
to the Director
’s [Film]
collection.
Am I misunderstanding how these relationships should work in SwiftData/CloudKit? It doesn’t make any sense to me that when two models are paired together that only one of them has a reference to the relationship, and the other has no knowledge of the link.
The above is a minimum reproducible example (and not my actual application). In my application, I basically compromised with the workaround that initially appears to be without consequence, but I have begun to notice crashes happening semi-regularly when deleting models that I suspect must be linked to setting the foundations incorrectly.