SwiftData单个视图@Query报错需设置modelContext的问题求助
SwiftData错误:“Set a .modelContext in view's environment to use Query”排查与解决
问题场景
已在主WindowGroup上绑定ModelContainer,其他视图使用@Query属性均正常,但SavedRecipesView下的RecipeList视图每次运行都会触发错误:Set a .modelContext in view's environment to use Query。
RecipeList与正常运行的Meals部分代码几乎一致,仅多了初始化displayedRecipes的逻辑(用于本地搜索过滤,避免重复查询)。
相关代码
主应用文件SwiftDataTestApp
@main struct SwiftDataTestApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([Meal.self, Recipe.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) }
ContentView代码
struct ContentView: View { @Environment(\.modelContext) var modelContext // 非必需,仅保留原有代码 @StateObject var state: AppState = AppState.shared() @State var selected: Int = 0 var body: some View { TabView(selection: $selected) { HomeView() .tabItem { Label("Home", systemImage: "house.fill") } .tag(0) SavedMealsView() .tabItem { Label("Meals", systemImage: "square.and.arrow.down.on.square") } .tag(1) SavedRecipesView() .tabItem { Label("Recipes", systemImage: "list.bullet.circle") } .tag(2) } } }
SavedRecipesView代码
struct SavedRecipesView: View { @Environment(NavigationContext.self) private var navigationContext @Environment(\.modelContext) private var modelContext // 非必需,仅保留原有代码 var body: some View { @Bindable var navigationContext = navigationContext NavigationSplitView { MealTypeListView().navigationTitle("Saved Recipes") } content: { RecipeListView(mealType: navigationContext.selectedMealType) .navigationTitle("\(navigationContext.selectedMealType?.rawValue ?? "") Recipes") } detail: { NavigationStack { SavedRecipeView(recipe: navigationContext.selectedRecipe) } } } }
RecipeListView及内部RecipeList代码
struct RecipeListView: View { let mealType: MealType? var body: some View { if let mealType { RecipeList(mealType: mealType) } else { ContentUnavailableView("Select A Meal Type", systemImage: "sidebar.left") } } } struct RecipeList: View { let mealType: MealType @Environment(NavigationContext.self) private var navigationContext @Environment(\.modelContext) private var modelContext @Query(sort: \Recipe.name) private var recipes: [Recipe] @State var searchText: String = "" @State var displayedRecipes: [Recipe] init(mealType: MealType) { self.mealType = mealType if mealType != .All { let predicate = #Predicate<Recipe> { $0.type == mealType.rawValue } _recipes = Query(filter: predicate, sort: \.name, order: .forward) } _displayedRecipes = State(initialValue: _recipes.wrappedValue) // 问题根源所在 } var body: some View { // 展示recipes的代码 } }
问题根源
错误出在RecipeList的初始化方法中直接访问_recipes.wrappedValue:
- 在视图的
init阶段,实例尚未被插入到视图树中,环境变量(包括modelContext)还未注入到视图实例里。 @Query依赖环境中的modelContext来获取数据,此时访问其wrappedValue会导致SwiftData无法找到所需的modelContext,从而抛出错误。- Meals部分没有这个初始化步骤,因此不会触发该问题。
解决方案
方案1:改用计算属性处理搜索过滤
直接基于recipes和searchText生成过滤后的结果,无需提前存储displayedRecipes,既简化代码又能自动响应数据变化:
struct RecipeList: View { let mealType: MealType @Environment(NavigationContext.self) private var navigationContext @Environment(\.modelContext) private var modelContext @Query(sort: \Recipe.name) private var recipes: [Recipe] @State var searchText: String = "" // 计算属性动态生成过滤结果 private var displayedRecipes: [Recipe] { let filteredByMealType = recipes if searchText.isEmpty { return filteredByMealType } else { return filteredByMealType.filter { $0.name.localizedCaseInsensitiveContains(searchText) } } } init(mealType: MealType) { self.mealType = mealType if mealType != .All { let predicate = #Predicate<Recipe> { $0.type == mealType.rawValue } _recipes = Query(filter: predicate, sort: \.name, order: .forward) } // 移除原有的displayedRecipes初始化代码 } var body: some View { List(displayedRecipes) { recipe in // 你的视图展示代码 } .searchable(text: $searchText) } }
方案2:延迟初始化displayedRecipes
如果需要保留@State存储displayedRecipes,可以在视图出现后再赋值,并监听数据变化更新:
struct RecipeList: View { let mealType: MealType @Environment(NavigationContext.self) private var navigationContext @Environment(\.modelContext) private var modelContext @Query(sort: \Recipe.name) private var recipes: [Recipe] @State var searchText: String = "" @State var displayedRecipes: [Recipe] = [] // 初始化为空数组 init(mealType: MealType) { self.mealType = mealType if mealType != .All { let predicate = #Predicate<Recipe> { $0.type == mealType.rawValue } _recipes = Query(filter: predicate, sort: \.name, order: .forward) } } var body: some View { List(displayedRecipes) { recipe in // 你的视图展示代码 } .searchable(text: $searchText) .onAppear { // 视图出现时赋值,此时环境已就绪 updateDisplayedRecipes() } .onChange(of: recipes) { _ in // 监听recipes数据变化,更新显示列表 updateDisplayedRecipes() } .onChange(of: searchText) { _ in // 监听搜索文本变化,更新显示列表 updateDisplayedRecipes() } } // 封装过滤逻辑 private func updateDisplayedRecipes() { var result = recipes if !searchText.isEmpty { result = result.filter { $0.name.localizedCaseInsensitiveContains(searchText) } } displayedRecipes = result } }
内容的提问来源于stack exchange,提问作者Neglected Sanity
相关产品推荐
相关产品推荐

