SwiftUI中FetchRequest有10条数据却不显示列表内容的问题
我在开发SwiftUI应用时遇到以下问题:在ServicesView中使用@FetchRequest获取Category实体数据,控制台通过Category.numberOfEntities()打印出有10条实体,但列表却显示为空(显示“Nothing here”)。
相关代码如下:
struct ServicesView: View { @FetchRequest(entity: Category.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Category.name, ascending: false)], animation: .easeIn) var results: FetchedResults<Category> var body: some View { List(results) { category in VStack(alignment: .leading, spacing: 3, content: { Text(category.name) .font(.system(size: 12)) .foregroundColor(.white) Text("x\(category.services.count)") }) } .overlay( Text(results.isEmpty ? "Nothing here" : "") ) .onAppear { print("😋") print(Category.numberOfEntities()) } } }
控制台输出:
😋 10
我猜测问题在于未给视图分配@Environment的managedObjectContext,相关的计数逻辑代码如下:
extension NSManagedObjectContext { static func defaultContext() -> NSManagedObjectContext { CoreDataManager.shared.defaultContext! } } protocol Fetchable { associatedtype FetchableType: NSManagedObject = Self static func numberOfEntities() -> Int } extension Fetchable where Self: NSManagedObject { static var entityName: String { entity().name ?? "" } static func numberOfEntities() -> Int { let request = NSFetchRequest<FetchableType>(entityName: entityName) let context = NSManagedObjectContext.defaultContext() do { return try context.count(for: request) } catch let error as NSError { print("❌ Core Data Count Error \(error), \(error.userInfo)") return 0 } } }
请问如何在App入口的代码中给ServicesView分配managedObjectContext?相关入口代码如下:
@main struct Watch_AppApp: App { var body: some Scene { WindowGroup { TabView { TimerView() ContentView() ServicesView() // how do I assign here managed object context? Text("4th page") } .tabViewStyle(PageTabViewStyle()) .onAppear(perform: loadData) //load data, fetch categories from cloudkit and save to core data, is is fetched and saved } } }
解决方案
你的猜测正确,@FetchRequest默认从视图环境中读取managedObjectContext,如果没有注入,它使用的上下文和你保存数据的CoreDataManager.shared.defaultContext不是同一个,导致数据不同步。
1. 在App入口注入Managed Object Context
在App结构体中获取CoreDataManager的上下文,通过.environment(\.managedObjectContext, context)注入到整个视图层级:
修改后的App入口代码:
@main struct Watch_AppApp: App { // 获取你的CoreDataManager提供的默认上下文 private let managedContext = CoreDataManager.shared.defaultContext! var body: some Scene { WindowGroup { TabView { TimerView() ContentView() ServicesView() Text("4th page") } .tabViewStyle(PageTabViewStyle()) // 将上下文注入到TabView及所有子视图的环境中 .environment(\.managedObjectContext, managedContext) .onAppear(perform: loadData) } } func loadData() { // 你的数据加载逻辑:从CloudKit获取分类并保存到Core Data } }
2. (可选)显式指定@FetchRequest的上下文
注入环境后,@FetchRequest会自动使用环境中的上下文,无需修改ServicesView。如果需要显式指定(多上下文场景),可以修改ServicesView的@FetchRequest:
struct ServicesView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest( entity: Category.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Category.name, ascending: false)], animation: .easeIn, context: viewContext ) var results: FetchedResults<Category> // 剩余代码保持不变 }
核心原因说明
你的numberOfEntities()方法使用的是CoreDataManager.shared.defaultContext,而未注入环境时,@FetchRequest使用的是默认上下文(通常是NSPersistentContainer的viewContext,和你自定义的defaultContext不一致),导致两边读取的是不同的上下文实例,数据无法同步。注入环境后,整个视图树共享同一个上下文,@FetchRequest就能正确读取到保存的数据。
内容的提问来源于stack exchange,提问作者kunass2

