WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

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

2      

Thanks Obelix, found different answers across the internet and only yours was correct. Initializing the container on the preview code fixed this!

   

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.