SwiftData Enums in Xcode Previews: Technical Findings
Notes and tips on how get these two work nicely together when using Enum with associated values.

The issue
SwiftData models containing enums with associated values cause intermittent crashes during delete operations in Xcode previews
Affected Configuration
- SwiftData models with enum properties that have associated values
- Delete operations in Xcode previews
- All iOS versions (17.0+)
Example Code That Crashes
enum Priority: Codable {
case low
case medium
case high(dueDate: Date)
case urgent(dueDate: Date, reason: String)
}
@Model
class TodoItem {
var id = UUID()
var title: String
var priority: Priority // ❌ Causes intermittent crashes on delete
var isCompleted = false
}
Technical Details
Crash Signature
libswiftCore.dylib: swift::swift_dynamicCastFailure
SwiftData: Dynamic cast failure during delete operation
Crash Characteristics
- Intermittent: May work initially, fails unpredictably
- Preview-specific: Only occurs in Xcode previews, not runtime
- Delete-specific: Only triggered by delete operations
- Type-casting related: SwiftData’s internal dynamic cast fails
Working Solution
Approach: Decompose Enum Storage
@Model
class TodoItem {
var id = UUID()
var title: String
// Store enum components separately
private var priorityType: String = "medium"
private var priorityDueDate: Date?
private var priorityReason: String?
// Reconstruct enum via computed property
var priority: Priority {
get {
switch priorityType {
case "low": return .low
case "medium": return .medium
case "high":
return .high(dueDate: priorityDueDate ?? Date())
case "urgent":
return .urgent(
dueDate: priorityDueDate ?? Date(),
reason: priorityReason ?? ""
)
default: return .medium
}
}
set {
switch newValue {
case .low:
priorityType = "low"
priorityDueDate = nil
priorityReason = nil
case .medium:
priorityType = "medium"
priorityDueDate = nil
priorityReason = nil
case .high(let date):
priorityType = "high"
priorityDueDate = date
priorityReason = nil
case .urgent(let date, let reason):
priorityType = "urgent"
priorityDueDate = date
priorityReason = reason
}
}
}
}
Preview Configuration (iOS 18.0+) Using PreviewModifier
@MainActor
struct TodoPreviewModifier: PreviewModifier {
static func makeSharedContext() throws -> ModelContainer {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(for: TodoItem.self, configurations: config)
container.mainContext.autosaveEnabled = false
return container
}
func body(content: Content, context: ModelContainer) -> some View {
content.modelContainer(context)
}
}
#Preview(traits: .modifier(TodoPreviewModifier())) {
TodoListView()
}
Basic Preview Setup
#Preview {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: TodoItem.self, configurations: config)
container.mainContext.autosaveEnabled = false
return TodoListView()
.modelContainer(container)
}
Key Findings
1. Enum Storage: SwiftData cannot reliably handle enums with associated values during delete operations
2. Decomposition Works: Storing enum data as separate properties avoids the crash
3. API Preservation: Computed properties maintain the same public interface
4. Preview-Specific: Issue only occurs in Xcode previews, not in production
5. Intermittent Nature: Crash may not occur immediately, making it hard to detect
Recommendations
When to Use This Pattern
- SwiftData models with enums containing associated values
- When delete operations are required
- For reliable Xcode previews
Benefits
- Eliminates preview crashes
- Enables property-based queries
- Maintains existing API
- Future-proof solution
Additional Advantages
- Can query on individual enum components
- Simpler migration if schema changes
- More explicit storage format