为 Xcode 预览创建具有派生属性的内存中持久存储的替代方法(iOS 17+)

问题描述 投票:0回答:1

我正在开发一个开发 iOS 应用程序和小部件扩展(iOS 17+ 和 Xcode 15)的项目,并使用 coreData 来管理用户数据,我的用户实体有一个“lastModify: Date”派生属性,但是-内存持久存储不允许派生属性有参数,当我尝试为预览提供程序创建虚拟数据时,它会导致应用程序崩溃,错误消息:

UserInfoWidgetExtension crashed due to an uncaught exception 'NSInvalidArgumentException'. Reason: Core Data provided atomic stores do not support derived

我的代码:

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)
    }
}

最终我删除了派生属性,它工作得很好,但我想知道是否有其他方法来避免这个问题?有没有办法预览具有派生属性的 coreData 实体?谢谢你。

12月1日更新: 崩溃的错误信息是:

UserInfoWidgetExtension crashed due to an uncaught exception 'NSInvalidArgumentException'. Reason: Core Data provided atomic stores do not support derived
。 并更新 UserInfoWidget.swift 的代码。

15/1更新: 谢谢@loremipsum的建议,它与以下代码配合得很好:

let dummyContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "BarcodeModel")
    let description = NSPersistentStoreDescription()
    //description.type = NSInMemoryStoreType
    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 = Barcode(context: container.viewContext)
    dummy.title = "dummy title"
    dummy.textCode = "1234-abcd"
    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)
swift xcode core-data derived
1个回答
0
投票

有两种方法可以拥有内存容器。

一种是使用

NSInMemoryStoreType

description.type = NSInMemoryStoreType

另一种是将 url 设置为

null

description.url = URL(fileURLWithPath: "/dev/null")

第一种方式已知有很多限制,例如级联删除,第二种方式是Apple用来在Xcode创建的标准

PersistentController
中创建预览容器。

© www.soinside.com 2019 - 2024. All rights reserved.