问题描述
在SwiftUI中,当配置Core Data后,运行Core Data相关命令,例如增删操作时,就可能会报错。

例如,在SwiftUI的View视图中,配置了Core Data代码。当点击添加按钮时,调用addFetchedResult(context: viewContext)方法。
func addFetchedResult(context: NSManagedObjectContext) {
let newEurofxrefhist = Eurofxrefhist(context: context)
newEurofxrefhist.date = Date()
newEurofxrefhist.currencySymbol = "USD"
newEurofxrefhist.exchangeRate = 7
do {
try context.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}

Xcode就会提示预览报错。
使用iPhone真机运行,应用没有报错或闪退,因此可以推断问题出在#Preview预览代码。
修改预览代码
#Preview {
ContentView()
}
预览代码中只有一个视图,可能是因为缺失了Core Data上下文环境导致的预览报错。
#Preview {
// 创建 NSPersistentContainer
let container = NSPersistentContainer(name: "ExchangeRateDataModel")
// 存储在内存中,防止写入磁盘
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
// 加载持久化存储
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
return ContentView()
.environment(\.managedObjectContext,container.viewContext)
}
在#Preview预览代码中配置NSPersistentContainer,并将viewContext通过environment传递给ContentView视图后,Xcode预览发生了报错。

解决方案
当我预加载一些数据进行预览时,Xcode预览不再报错。
// 预加载一些数据进行预览
let context = container.viewContext
let sampleData = Eurofxrefhist(context: context)
sampleData.date = Date()
sampleData.currencySymbol = "USD"
sampleData.exchangeRate = 7.0
do {
try context.save()
} catch {
print("Error saving preview data: \(error.localizedDescription)")
}

总结
@FetchRequest 依赖于 NSManagedObjectContext 提供的持久化存储进行数据获取。如果数据库是空的,FetchRequest 会尝试查询数据,但返回一个空的 FetchedResults,并且在 #Preview 环境下,这种情况可能导致未预料的行为,比如:
exchangeRates.isEmpty 触发 UI 更新时没有数据,导致 UI 出现异常。
ForEach 在没有数据的情况下会尝试渲染空视图,预览时容易引发不可预料的错误。
Core Data 的持久化容器没有数据时,@FetchRequest 可能无法成功触发数据绑定。
Core Data预览报错的问题,最终在#Preview中配置Core Data上下文环境以及新增示例数据的情况下,得到解决。
后续涉及Core Data预览时,应该考虑添加示例数据,防止 FetchRequest 返回空值。
如果有多个实体或复杂数据,可以使用 PersistenceController.preview 进行统一管理。