0

Below is the minimum code that causes the crash. What I want to achieve: pass a default AchievementB to the view and then change (based on user input) and insert into achievements.

update: after a few attempts, it seems that i can't init the object before the view is appear, but i don't know why.

import SwiftData
import SwiftUI

@Model
class AchievementB: Identifiable {
  let id: UUID = UUID()

  init(id: UUID = UUID()) {
      self.id = id
  }
}

struct AchievementView: View {
  @State var options: AchievementB

  @Environment(\.modelContext) private var modelContext
  @Query var achievements: [AchievementB]
  
  var body: some View {
    Text("hello")
  }
}

#Preview {
  AchievementView(options: AchievementB())
    .modelContainer(for: AchievementB.self)
}
7
  • So you are running this in Xcode Previews? Commented May 28, 2024 at 7:41
  • that is correct Commented May 28, 2024 at 7:41
  • It doesn't crash for me. Did you set up the ModelContainer properly for the app itself? Commented May 28, 2024 at 8:17
  • that's all the code i have. what kind of setup are you referring to? Commented May 28, 2024 at 8:25
  • In your app code in <my app name>.swift you must also add a .modelContainer modifier to the WindowGroup Commented May 28, 2024 at 8:28

1 Answer 1

2

In your code, the AchievementB object is created before a model container exists. AchievementB() is executed first, before the .modelContainer(for: ...) modifier, which creates the model container.

Before you create any @Model objects, you should first create a model container, because the @Model objects needs a model container for their properties to work. When you get/set a property of a @Model object, the actual reading and writing of the data is delegated to the model container.

You should manually create a ModelContainer first, then add it to the view using the other overload of the modelContainer modifier.

#Preview {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: AchievementB.self, configurations: config)
    return AchievementView(options: AchievementB())
        .modelContainer(container)
}

Note that you need to explicitly return because #Preview is not a view builder.

Side note: options should not be a @State if you are going to pass things to it. It should just be a regular let/var, or @Bindable if you want to get Bindings out of it.

Sign up to request clarification or add additional context in comments.

2 Comments

With Xcode 15.4 this doesn't seem to be needed and having only AchievementView(options: AchievementB()).modelContainer(for: AchievementB.self, inMemory: true) in the #Preview macro works fine.
@JoakimDanielson Ah that's interesting. I (and probably the OP too) am on Xcode 15.3.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.