iOS17+ Xcode预览含派生属性的Core Data内存存储替代方案
问题:Core Data内存存储不支持派生属性导致Widget预览崩溃
开发适配iOS17+与Xcode15的iOS应用及Widget扩展时,使用Core Data管理用户数据,用户实体包含一个名为lastModify(Date类型)的派生属性。在为预览提供器创建测试数据时应用崩溃,错误信息如下:
UserInfoWidgetExtension crashed due to an uncaught exception 'NSInvalidArgumentException'. Reason: Core Data provided atomic stores do not support derived
原Widget代码如下:
import WidgetKit import SwiftUI import Intents import AppIntents import CoreData struct WidgetDetailsProvider: AppIntentTimelineProvider { func placeholder(in context: Context) -> SimpleEntry { return SimpleEntry(date: .now) } func snapshot(for configuration: UserInfoWidgetIntent, in context: Context) async -> SimpleEntry { return SimpleEntry(date: .now) } func timeline(for configuration: UserInfoWidgetIntent, in context: Context) async -> Timeline<SimpleEntry> { let entry = SimpleEntry(date: Date()) let timeline = Timeline(entries: [entry], policy: .never) return timeline } } struct UserInfoWidgetIntent: WidgetConfigurationIntent { static var title: LocalizedStringResource = "Test" static var description: IntentDescription = IntentDescription("Test description") @Parameter(title: "Display in full name") var isDisplayFullname: Bool init(isDisplayFullname: Bool) { self.isDisplayFullname = isDisplayFullname } init() {} } struct SimpleEntry: TimelineEntry { let date: Date let isDisplayFullname: Bool } struct UserInfoWidgetEntryView : View { var entry: WidgetDetailsProvider.Entry @FetchRequest(sortDescriptors: [SortDescriptor(\.lastModify, order: .reverse)]) var userInfo: FetchedResults<UserInfo> var body: some View { VStack { Text(entry.date, style: .time) .frame(alignment: .leading) HStack { if userInfo.isEmpty { Text("The list is Empty") } ForEach(userInfo) {user in if entry.isDisplayFullname { Text(user.fullname) } else { Text(user.name) } } } }.containerBackground(for: .widget) { Color.white } } } struct UserInfoWidget: Widget { let kind: String = "UserInfoWidgetIntent" var body: some WidgetConfiguration { AppIntentConfiguration( kind: kind, intent: UserInfoWidgetIntent.self, provider: WidgetDetailsProvider()) { entry in UserInfoWidgetEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) } } struct UserInfoWidget_Previews: PreviewProvider { static var previews: some View { let dummyContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "UserModel") let description = NSPersistentStoreDescription() description.type = NSInMemoryStoreType container.persistentStoreDescriptions = [description] container.loadPersistentStores {description, error in if let error = error { print("Failed to load: \(error)") } } let dummy = Barcode(context: container.viewContext) dummy.username = "foo" do { try container.viewContext.save() } catch { fatalError("Failed to save entity.") } return container }() UserInfoWidgetEntryView(entry: SimpleEntry(date: Date())) .previewContext(WidgetPreviewContext(family: .systemMedium)) .environment(\.managedObjectContext, dummyContainer.viewContext) } }
移除派生属性后应用恢复正常,但希望找到无需移除派生属性的解决方案,实现在预览中使用带有派生属性的Core Data实体。
解决方案
将预览中的内存存储(NSInMemoryStoreType)替换为指向/dev/null的本地存储,这样既不会持久化测试数据,又能支持派生属性。修改后的预览代码如下:
struct UserInfoWidget_Previews: PreviewProvider { static var previews: some View { let dummyContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "UserInfoModel") let description = NSPersistentStoreDescription() // 替换内存存储为指向/dev/null的本地存储 description.url = URL(fileURLWithPath: "/dev/null") container.persistentStoreDescriptions = [description] container.loadPersistentStores {description, error in if let error = error { print("Failed to load: \(error)") } } let dummy = UserInfo(context: container.viewContext) dummy.name = "foo" dummy.fullname = "dummy foo" do { try container.viewContext.save() } catch { fatalError("Failed to save entity.") } return container }() UserInfoWidgetEntryView(entry: SimpleEntry(date: Date())) .previewContext(WidgetPreviewContext(family: .systemSmall)) .environment(\.managedObjectContext, dummyContainer.viewContext) } }
内容的提问来源于stack exchange,提问作者KPB98115
相关产品推荐
相关产品推荐

