I've created a basic database model in SwiftData to show an issue I'm encountering; a parent class and child class, with a relationship.
This is displayed in a simple view.
When I try and preview this in the canvas in Xcode, it crashes. If I comment out the item.parent = .preview, it doesn't crash.
Everything below (and including) the actor PreviewSampleData { is based on Apple's SwiftData code examples.
Any help would be greatly appreciated. Many thanks.
import SwiftUI
import SwiftData
struct ContentView: View {
var child: Child
var body: some View {
VStack {
Text("\(child.name)")
Text("\(child.parent?.name ?? "No name")")
}
.padding()
}
}
#Preview {
ModelContainerPreview(PreviewSampleData.inMemoryContainer) {
ContentView(child: .preview)
}
}
@Model final class Parent {
var name: String
@Relationship(deleteRule: .cascade, inverse: \Child.parent)
var children: [Child] = [Child]()
init(name: String) {
self.name = name
}
}
extension Parent {
static var preview: Parent {
Parent(name: "Parent 1")
}
}
@Model final class Child {
var name: String
var parent: Parent?
init(name: String) {
self.name = name
}
}
extension Child {
static var preview: Child {
let item = Child(name: "Child 1")
item.parent = .preview
return item
}
}
actor PreviewSampleData {
@MainActor
static var container: ModelContainer = {
return try! inMemoryContainer()
}()
static var inMemoryContainer: () throws -> ModelContainer = {
let schema = Schema([Parent.self, Child.self])
let configuration = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: schema, configurations: [configuration])
let sampleData: [any PersistentModel] = [
Parent.preview, Child.preview
]
Task { @MainActor in
sampleData.forEach {
container.mainContext.insert($0)
}
}
return container
}
}
struct ModelContainerPreview<Content: View>: View {
var content: () -> Content
let container: ModelContainer
init(@ViewBuilder content: @escaping () -> Content, modelContainer: @escaping () throws -> ModelContainer) {
self.content = content
do {
self.container = try MainActor.assumeIsolated(modelContainer)
} catch {
fatalError("Failed to create the model container: \(error.localizedDescription)")
}
}
init(_ modelContainer: @escaping () throws -> ModelContainer, @ViewBuilder content: @escaping () -> Content) {
self.init(content: content, modelContainer: modelContainer)
}
var body: some View {
content()
.modelContainer(container)
}
}
"I want a single model container I fully populate with test data, so I can use this anywhere in my code."
Apple's solution is a bit complex.
Simple Way to Add Mock Data
Here is another way to create a container and add mock data at the same time:
Now this static property is on your model that it creates mock data for.
How to use
Since
previewreturns aModelContainer, you can use it with themodelContainermodifier to power your previews with mock data!