I try to add an enum field to my model:
extension Item {
enum Diet: String, CaseIterable, Codable {
case herbivorous = "Herbivore"
case carnivorous = "Carnivore"
case omnivorous = "Omnivore"
}
}
@Model
final class Item {
var name: String = "New item"
var createdAt: Date = Date()
var diet: Diet = Diet.herbivorous // <-- new field
init(name: String) {
self.name = name
}
}
But I keep getting a runtime error:
Fatal error: Passed nil for a non-optional keypath \Item.diet
Making the field optional solves the problem, or making it a String type, but I'm trying to find out why this is not working. It looks like the diet field from: https://developer.apple.com/documentation/swiftdata/defining-data-relationships-with-enumerations-and-model-classes. So my main question is: what am I doing wrong?
I'm new with SwiftData so if there is a nice way to debug this myself, let me know. The magic behind the scenes annoys me a lot but I haven't found a way to visualize my migrations or database state yet.
Steps to reproduce:
The migration is performed but only in the sense that the new field is added to the database table but no default value is written so all rows has NULL for this field. And this is what later on creates a crash when SwiftData tries to assign nil
to a non-optional property. It looks like a lightweight migration isn't working in this scenario when it actually should.
A workaround here is to not declare the persisted property to be of type Diet
but instead the raw value for the enum, String
. Then we can declare this property as private and instead use a computed property of type Diet
as the public one
private var dietRaw: Diet.RawValue = Diet.herbivorous.rawValue
var diet: Diet {
get { .init(rawValue: dietRaw) ?? .herbivorous }
set { dietRaw = newValue.rawValue }
}
init(name: String, diet: Diet = .herbivorous ) {
self.name = name
self.dietRaw = diet.rawValue
}