swiftuiswiftui-environment

Preview canvas crashing because environmentObject not set properly


I have my preview canvas crashing because the environmentObject is not properly set. The app itself builds and run properly.

Error message.

app crashed due to missing environment of type: TaskModel. To resolve this add .environmentObject(TaskModel(...)) to the appropriate preview.

My View with the #Preview macro looks like this.

struct CopyTaskListScreen: View {
    @EnvironmentObject private var taskModel: TaskModel
    
    var body: some View {
        NavigationStack {
            List(taskModel.tasks.sorted(by: <), id: \.key) { task in
                Text(task.value.title)
            }
        }
    }
}
#Preview {
    CopyTaskListScreen()
        .environmentObject(TaskModel(//how to call TaskDataService here?))
}

My TaskModel looks the following. I inject the TaskDataService with the ITaskData protocol. The TaskDataService accepts all data adapters implementing the ITaskData protocol and hand it over to my TaskModel.

@MainActor
class TaskModel: ObservableObject {
    @Published var tasks: [UUID: Task] = [:]
    
    var data: TaskDataService
    
    init(data: ITaskData) {
        self.data = TaskDataService(data: data)
        readTasks()
    }

I tried to inject the environment in #Preview the same way I did it in @main. But with the same result. Preview canvas is crashing.

@main
struct mTaskApp: App {

    var body: some Scene {
        WindowGroup {
            MainScreen()
              .environmentObject(TaskModel(data: TaskDataService(data: RemindersDataAdapter())))

Solution

  • Normally your service would be an EnvironmentKey, e.g.

    @Environment(\.taskDataService) var taskDataService
    @EnvironmentObject private var taskModel: TaskModel
    

    And would use it like this

    .task {
        await taskDataService.load(into: taskModel)
    }
    

    Or better (where results is State):

    .task {
        results = await taskDataService.load()
    }
    

    Then in preview it would be:

    #Preview {
        CopyTaskListScreen()
            .environment(\.taskDataService, MockTaskDataService())
            .environmentObject(TaskModel())
    }
    
    protocol TaskDataService{}
    struct RealTaskDataService: TaskDataService{}
    struct MockTaskDataService: TaskDataService{}