TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Day 54 - Crash in preview of SwiftData

Forums > 100 Days of SwiftUI

I'm getting a crash in the preview canvas only - runs fine in the simulator. Pretty sure I've copied Paul's code exactly, but cannot get past this error:

Fatal error in ModelContainer.swift - failed to find a currently active container for Book.

My Code

Main App Bundle:

import SwiftUI
import SwiftData

@main
struct SwiftDataSandboxApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Book.self, inMemory: true)
    }
}

ContentView

import SwiftData
import SwiftUI

struct ContentView: View {
    @Environment(\.modelContext) var modelContext
    @Query var books: [Book]

    @State private var showingAddScreen = false

    var body: some View {
        NavigationStack {
           Text("Count: \(books.count)")
               .navigationTitle("Bookworm")
               .toolbar {
                   ToolbarItem(placement: .topBarTrailing) {
                       Button("Add Book", systemImage: "plus") {
                           showingAddScreen.toggle()
                       }
                   }
               }
               .sheet(isPresented: $showingAddScreen) {
                   AddBookView()
               }
       }
    }
}

#Preview {
    ContentView()
}

AddBookView

import SwiftUI

struct AddBookView: View {
    @Environment(\.modelContext) var modelContext
    @Environment(\.dismiss) var dismiss

    @State private var title = ""
    @State private var author = ""
    @State private var rating = 3
    @State private var genre = "Fantasy"
    @State private var review = ""

    let genres = ["Fantasy", "Horror", "Kids", "Mystery", "Poetry", "Romance", "Thriller"]

    var body: some View {
        NavigationStack {
            Form {
                Section {
                    TextField("Name of book", text: $title)
                    TextField("Author's name", text: $author)

                    Picker("Genre", selection: $genre) {
                        ForEach(genres, id: \.self) {
                            Text($0)
                        }
                    }
                }

                Section("Write a review") {
                    TextEditor(text: $review)

                    Picker("Rating", selection: $rating) {
                        ForEach(0..<6) {
                            Text(String($0))
                        }
                    }
                }

                Section {
                    Button("Save") {
                        let newBook = Book(title: title, author: author, genre: genre, review: review, rating: rating)
                        modelContext.insert(newBook)
                        dismiss()
                    }
                }
            }
            .navigationTitle("Add Book")
        }
    }
}

#Preview {
    AddBookView()
}

Book

import Foundation
import SwiftData

@Model
class Book {
    var title: String
    var author: String
    var genre: String
    var review: String
    var rating: Int

    init(title: String, author: String, genre: String, review: String, rating: Int) {
        self.title = title
        self.author = author
        self.genre = genre
        self.review = review
        self.rating = rating
    }
}

I've also tried initiating the modelContainer differenly in the main app file as I found in other suggestions online. This did not change the crash.

@main
struct SwiftDataSandboxApp: App {

  let modelContainer: ModelContainer

  init() {
    do {
      modelContainer = try ModelContainer(for: Book.self)
    } catch {
      fatalError("Could not initialize ModelContainer")
    }
  }

  var body: some Scene {
    WindowGroup {
      VStack {
        ContentView()
      }
    }
    .modelContainer(modelContainer)
  }
}

Any help is apprectiated!!

   

When you run an application in the simulator, all the pieces of all your code files are available to execute and provide data between themselves.

So when your app decides to compute the body of the ContentView struct, it will call on the Environment to wire itself to the modelContent variable that was initiated by the SwiftDataSandboxApp at start up.

You see? The SwiftDataSandboxApp code was executed and generated a modelContainer. The ContentView shares that data when it tries to render its body variable.

Preview

But if you're just wanting to see just the body of the ContentView in a Preview, ask yourself "Where is ContentView going to get its Environment variable from???" How is it going to wire up a link to the modelContent var in the Environment?

A Preview only knows about the view that's inside the #Preview macro. You did not initialize a modelContainer in your #Preview macro. So the preview will crash whilst accurately reporting that it doesn't have a modelContainer with a Book class.

Keep Coding

For a great tutorial on viewing SwiftData in Previews, jump over to Stewart Lynch's series on SwiftData in the YouTube.

See -> SwiftData Previews

1      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.