不存在的核心数据管理对象属性的NSunknownyKeyException

发布于 2025-02-12 01:40:48 字数 12342 浏览 1 评论 0原文

我有一个依赖核心数据持续存在的应用程序。 该应用程序正在使用MVVM架构以促进测试。

开发环境是macBook pro(m1 max)运行macOS 12.4xcode 14 beta 2

到目前为止,一切都很好,直到我尝试尝试要将一个新的Entity添加到与另一个实体有关系(多一对)的模型中。试图实例化这些实体之一,导致与定义关系的属性关联的nsunknownekyexception,因此我从模型中删除了此关系>,并重新运行了测试。

令我惊讶的是,错误持续存在,而且我一直无法解决它,因为尽管有以下内容:

  • 清洁构建文件夹
  • 删除派生数据,重新启动xcode XCODE
  • 重命名cristendentity(错误)坚持不懈,仅具有不同的实体名称)
  • delete entity,然后添加到模型
  • 关闭代码 - 基因并定义MO子类手动
  • 检查核心数据xml-这看起来无可示感并且没有任何残留属性可能导致
  • 来自模拟器
  • 擦除设置的问题删除应用程序,然后重新启动模拟器
  • 卸载,然后重新安装xcode

我尚未尝试过的一件事就是通过xcode 13运行代码(这是一台新计算机因此,我都很热情,直接去找beta,这可能是一个错误!),因此很快就会这样做。

目前看来,与核心数据模型和实体之间的关系有关的东西似乎藏在我找不到的地方,但这可能是工具中的错误。当然,问题是我无法继续使用编译器错误来开发该应用程序。任何想法或指导都会非常欢迎...

Xcode模型接口

Xcode模型接口

.xcdatamodeld XML用于处方

测试期间的错误消息

在有问题的托管托管子类(便利方法)上扩展:


extension Prescription {

    // MARK: - INTERNAL TYPES

    enum PointOfCareContext: String, CustomStringConvertible {

        case premedication, perioperative, postoperative, hospitalised



        var description: String {

            return self.rawValue.capitalized

        }

    }



    // MARK: - PROPERTIES

    var drug: Drug {

        get { guard let drug = cd_drug else {

            fatalError("Persistent store corrupted: Prescription is missing relationship to Drug.")

        }

            return drug

        }

        set { cd_drug = newValue }

    }

    

    var timePrescribed: Date {

        get { guard let timePrescribed = cd_timePrescribed else {

            fatalError("Persistent store corrupted: Prescription is missing timePrescribed.")

        }

            return timePrescribed

        }

        set { cd_timePrescribed = newValue }

    }





    /// When the drug should be administered

    /// Passing nil to this property indicates the drug should be given asap (so will return current Date)

    var timeScheduled: Date? {

        get { guard let timeScheduled = cd_timeScheduled else {

            return Date.now

        }

            return timeScheduled

        }

        set { cd_timeScheduled = newValue }

    }



    /// When the drug was administered

    /// Nil indicates not given yet

    var timeAdministered: Date? {

        get { return cd_timeAdministered }

        set { cd_timeScheduled = newValue }

    }



    var doseRate: Measurement<UnitDensity> {

        get {

            if (cd_doseRate != 0) {

                fatalError("Persistent store corrupted: Prescription doseRate == 0.")

            }

            return Measurement(value: cd_doseRate, unit: .milligramsPerKilogram)

        }

        set {

            cd_doseRate = newValue.converted(to: .milligramsPerKilogram).value

        }

    }



    var dose: Measurement<UnitMass> {

        get {

            return Measurement(value: cd_dose, unit: .milligrams)

        }

        set {

            cd_doseRate = newValue.converted(to: .milligrams).value

        }

    }



    var doseVolume: Measurement<UnitVolume> {

        get {

            return Measurement(value: cd_doseVolume, unit: .milliliters)

        }

        set {

            cd_doseVolume = newValue.converted(to: .milliliters).value

        }

    }



    var context: PointOfCareContext {

        get {

            guard let contextValue = cd_context, let context = PointOfCareContext(rawValue: contextValue) else {

                fatalError("Persistent store corrupted: Prescription has an invalid administration context [\(cd_context ?? "")]")

            }

            return context

        }

        set {

            cd_context = newValue.rawValue

        }

    }

视图模型,该模型是测试的主题:

extension ProcedurePlanView {
    class ViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
        // swiftlint:disable:next nesting
        typealias Dependencies = HasParameterController & HasPersistence & HasViewFactory

        private let parameterController: ParameterController
        private let persistenceService: PersistenceService
        private let viewFactory: ViewFactory
        private let frc: NSFetchedResultsController<Prescription>

        let patient: Patient
        let procedure: Procedure

        // MARK: - TabOne conformance
        @Published var clinician: String = ""
        @Published var anaesthetist: String = ""
        @Published var assistant: String = ""
        @Published var history: String = ""
        @Published var tasks: String = ""
        @Published var complications: String = ""
        @Published var mucousMembraneColor: MucousMembraneColor?
        @Published var asaGrade: ASAGrade?
        @Published var cannulaPosition: CannulaPosition?
        @Published var cannulaSide: Side?
        @Published var cannulaGauge: PeripheralCannulaGauge?
        @Published var auscultation: String = ""
        @Published var temperament: Double?
        @Published var airwayType: AirwayType?
        @Published var airwaySize: String?
        @Published var airwayCuffStatus: Bool?
        @Published var breathingSystem: BreathingSystemType?
        @Published var prescriptions: [Prescription]?

        // MARK: - INIT
        init(procedure: Procedure? = nil, patient: Patient, dependencies: Dependencies) {
            self.persistenceService = dependencies.persistenceService
            self.parameterController = dependencies.parameterController
            self.viewFactory = dependencies.viewFactory

            var currentProcedure: Procedure?
            if let procedure {
                currentProcedure = procedure
            } else {
                currentProcedure = Procedure(context: persistenceService.viewContext)
                currentProcedure!.patient = patient
            }

            let frc = ProcedurePlanView.ViewModel.createPremedFRCUsing(
                patient,
                procedure: currentProcedure!,
                context: dependencies.persistenceService.viewContext
            )

            self.frc = frc
            self.patient = patient
            self.procedure = currentProcedure!
            self.prescriptions = ProcedurePlanView.ViewModel.performInitialFetchFor(frc)
            
            super.init()
            self.frc.delegate = self
        }

        // MARK: - PUBLIC METHODS
        func makeParameterGridView() -> some View {
            viewFactory.makeParameterGridView(patient: patient)
        }

        func resetForm() {
            clinician.removeAll()
            anaesthetist.removeAll()
            assistant.removeAll()
            history.removeAll()
            tasks.removeAll()
            complications.removeAll()

            mucousMembraneColor = nil
            asaGrade = nil
            cannulaPosition = nil
            cannulaSide = nil
            cannulaGauge = nil
            auscultation.removeAll()
            temperament = nil
            airwayType = nil
            airwaySize = nil
            airwayCuffStatus = nil
            breathingSystem = nil
        }

        func save() {
            procedure.patient = patient
            procedure.clinican = clinician
            procedure.anaesthetist = anaesthetist
            procedure.assistant = assistant
            procedure.history = history
            procedure.tasks = tasks
            procedure.complications = complications
            procedure.asaGrade = asaGrade
            Logger.viewModel.warning("Incomplete implementaton of cannula data in Procedure managed object and VM")

            _ = Observation.create(
                in: persistenceService.viewContext,
                patient: patient,
                parameter: parameterController.getParameterWith(name: .mucousMembraneColor),
                stringValue: mucousMembraneColor?.rawValue ?? "Unknown"
            )

            do {
                try persistenceService.viewContext.saveIfNeeded()
            } catch {
                fatalError(
                    "Unable to save context \(persistenceService.viewContext) to store due to error: \(error.localizedDescription)"
                )
            }
        }
        
        // MARK: - PRIVATE METHODS
        private static func createPremedFRCUsing(_ patient: Patient, procedure: Procedure, context: NSManagedObjectContext) -> NSFetchedResultsController<Prescription> {
            let request = Prescription.fetchRequest()
            request.predicate = NSPredicate(format: "procedure == %@", procedure)
            request.sortDescriptors = [NSSortDescriptor(keyPath: \Prescription.cd_drug?.cd_nameGeneric, ascending: false)]

            let frc = NSFetchedResultsController(
                fetchRequest: request,
                managedObjectContext: context,
                sectionNameKeyPath: nil,
                cacheName: nil
            )
            
            return frc
        }
        
        private static func performInitialFetchFor(_ frc: NSFetchedResultsController<Prescription>) -> [Prescription] {
            do {
                try frc.performFetch()
            } catch {
                Logger.persistenceService.critical(
                    "FRC failed to fetch Drugs from persistent store: \(error.localizedDescription)"
                )
            }

            return frc.fetchedObjects ?? []
        }

        // MARK: - NSFetchedResultsControllerDelegate conformance
        internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            let newPrescriptions = controller.fetchedObjects as? [Prescription] ?? []
            prescriptions = newPrescriptions
        }
    }
}

I have an app that relies on Core Data for persistence.
The app is using MVVM architecture in order to facilitate testing.

The dev environment is a MacBook Pro (M1 Max) running macOS 12.4 and Xcode 14 beta 2

So far everything has been working well until I attempted to add a new entity to my Model that had a relationship (many-to-one) to another entity. Attempting to instantiate one of these entities resulted in a NSUnknownKeyException associated with the property that defined the relationship, so I deleted this relationship from the model and re-ran the test.

Much to my surprise, the error persisted and I have been singularly unable to resolve it since despite the following:

  • Clean build folder
  • Delete derived data, reboot XCode
  • Rename offending entity (error persists, just with the different entity name)
  • Delete entity and add back into the Model
  • Turn off code-gen and defined the MO subclass manually
  • Check Core Data XML - this looks unremarkable and there are no residual properties that could be causing the problem
  • Delete app from Simulator
  • Erase settings then restart Simulator
  • Uninstall and then re-install Xcode

The one thing I haven't tried yet is to run the code through Xcode 13 (it's a new machine so I was all enthusiastic and went straight for the beta which might have been a mistake!) so will be doing that shortly.

For now it seems that something relating to the Core Data model and relationships between entities is hiding out somewhere I can't find it but it could be a bug in the tooling. The problem of course is that I can't continue to develop the app with a compiler error I can't clear. Any thoughts or guidance would be very welcome...

Xcode Model interface for Prescription
Prescription entity in model editor

Xcode Model interface for Procedure
Procedure entity in model editor

.xcdatamodeld XML for Prescription
Section of XML in .xcdatamodeld file related to Prescription entity

Error message during test
Error associated in Prescription object during testing

Extension on the offending ManagedObject subclass (convenience methods):


extension Prescription {

    // MARK: - INTERNAL TYPES

    enum PointOfCareContext: String, CustomStringConvertible {

        case premedication, perioperative, postoperative, hospitalised



        var description: String {

            return self.rawValue.capitalized

        }

    }



    // MARK: - PROPERTIES

    var drug: Drug {

        get { guard let drug = cd_drug else {

            fatalError("Persistent store corrupted: Prescription is missing relationship to Drug.")

        }

            return drug

        }

        set { cd_drug = newValue }

    }

    

    var timePrescribed: Date {

        get { guard let timePrescribed = cd_timePrescribed else {

            fatalError("Persistent store corrupted: Prescription is missing timePrescribed.")

        }

            return timePrescribed

        }

        set { cd_timePrescribed = newValue }

    }





    /// When the drug should be administered

    /// Passing nil to this property indicates the drug should be given asap (so will return current Date)

    var timeScheduled: Date? {

        get { guard let timeScheduled = cd_timeScheduled else {

            return Date.now

        }

            return timeScheduled

        }

        set { cd_timeScheduled = newValue }

    }



    /// When the drug was administered

    /// Nil indicates not given yet

    var timeAdministered: Date? {

        get { return cd_timeAdministered }

        set { cd_timeScheduled = newValue }

    }



    var doseRate: Measurement<UnitDensity> {

        get {

            if (cd_doseRate != 0) {

                fatalError("Persistent store corrupted: Prescription doseRate == 0.")

            }

            return Measurement(value: cd_doseRate, unit: .milligramsPerKilogram)

        }

        set {

            cd_doseRate = newValue.converted(to: .milligramsPerKilogram).value

        }

    }



    var dose: Measurement<UnitMass> {

        get {

            return Measurement(value: cd_dose, unit: .milligrams)

        }

        set {

            cd_doseRate = newValue.converted(to: .milligrams).value

        }

    }



    var doseVolume: Measurement<UnitVolume> {

        get {

            return Measurement(value: cd_doseVolume, unit: .milliliters)

        }

        set {

            cd_doseVolume = newValue.converted(to: .milliliters).value

        }

    }



    var context: PointOfCareContext {

        get {

            guard let contextValue = cd_context, let context = PointOfCareContext(rawValue: contextValue) else {

                fatalError("Persistent store corrupted: Prescription has an invalid administration context [\(cd_context ?? "")]")

            }

            return context

        }

        set {

            cd_context = newValue.rawValue

        }

    }

View Model that is the subject of the test:

extension ProcedurePlanView {
    class ViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
        // swiftlint:disable:next nesting
        typealias Dependencies = HasParameterController & HasPersistence & HasViewFactory

        private let parameterController: ParameterController
        private let persistenceService: PersistenceService
        private let viewFactory: ViewFactory
        private let frc: NSFetchedResultsController<Prescription>

        let patient: Patient
        let procedure: Procedure

        // MARK: - TabOne conformance
        @Published var clinician: String = ""
        @Published var anaesthetist: String = ""
        @Published var assistant: String = ""
        @Published var history: String = ""
        @Published var tasks: String = ""
        @Published var complications: String = ""
        @Published var mucousMembraneColor: MucousMembraneColor?
        @Published var asaGrade: ASAGrade?
        @Published var cannulaPosition: CannulaPosition?
        @Published var cannulaSide: Side?
        @Published var cannulaGauge: PeripheralCannulaGauge?
        @Published var auscultation: String = ""
        @Published var temperament: Double?
        @Published var airwayType: AirwayType?
        @Published var airwaySize: String?
        @Published var airwayCuffStatus: Bool?
        @Published var breathingSystem: BreathingSystemType?
        @Published var prescriptions: [Prescription]?

        // MARK: - INIT
        init(procedure: Procedure? = nil, patient: Patient, dependencies: Dependencies) {
            self.persistenceService = dependencies.persistenceService
            self.parameterController = dependencies.parameterController
            self.viewFactory = dependencies.viewFactory

            var currentProcedure: Procedure?
            if let procedure {
                currentProcedure = procedure
            } else {
                currentProcedure = Procedure(context: persistenceService.viewContext)
                currentProcedure!.patient = patient
            }

            let frc = ProcedurePlanView.ViewModel.createPremedFRCUsing(
                patient,
                procedure: currentProcedure!,
                context: dependencies.persistenceService.viewContext
            )

            self.frc = frc
            self.patient = patient
            self.procedure = currentProcedure!
            self.prescriptions = ProcedurePlanView.ViewModel.performInitialFetchFor(frc)
            
            super.init()
            self.frc.delegate = self
        }

        // MARK: - PUBLIC METHODS
        func makeParameterGridView() -> some View {
            viewFactory.makeParameterGridView(patient: patient)
        }

        func resetForm() {
            clinician.removeAll()
            anaesthetist.removeAll()
            assistant.removeAll()
            history.removeAll()
            tasks.removeAll()
            complications.removeAll()

            mucousMembraneColor = nil
            asaGrade = nil
            cannulaPosition = nil
            cannulaSide = nil
            cannulaGauge = nil
            auscultation.removeAll()
            temperament = nil
            airwayType = nil
            airwaySize = nil
            airwayCuffStatus = nil
            breathingSystem = nil
        }

        func save() {
            procedure.patient = patient
            procedure.clinican = clinician
            procedure.anaesthetist = anaesthetist
            procedure.assistant = assistant
            procedure.history = history
            procedure.tasks = tasks
            procedure.complications = complications
            procedure.asaGrade = asaGrade
            Logger.viewModel.warning("Incomplete implementaton of cannula data in Procedure managed object and VM")

            _ = Observation.create(
                in: persistenceService.viewContext,
                patient: patient,
                parameter: parameterController.getParameterWith(name: .mucousMembraneColor),
                stringValue: mucousMembraneColor?.rawValue ?? "Unknown"
            )

            do {
                try persistenceService.viewContext.saveIfNeeded()
            } catch {
                fatalError(
                    "Unable to save context \(persistenceService.viewContext) to store due to error: \(error.localizedDescription)"
                )
            }
        }
        
        // MARK: - PRIVATE METHODS
        private static func createPremedFRCUsing(_ patient: Patient, procedure: Procedure, context: NSManagedObjectContext) -> NSFetchedResultsController<Prescription> {
            let request = Prescription.fetchRequest()
            request.predicate = NSPredicate(format: "procedure == %@", procedure)
            request.sortDescriptors = [NSSortDescriptor(keyPath: \Prescription.cd_drug?.cd_nameGeneric, ascending: false)]

            let frc = NSFetchedResultsController(
                fetchRequest: request,
                managedObjectContext: context,
                sectionNameKeyPath: nil,
                cacheName: nil
            )
            
            return frc
        }
        
        private static func performInitialFetchFor(_ frc: NSFetchedResultsController<Prescription>) -> [Prescription] {
            do {
                try frc.performFetch()
            } catch {
                Logger.persistenceService.critical(
                    "FRC failed to fetch Drugs from persistent store: \(error.localizedDescription)"
                )
            }

            return frc.fetchedObjects ?? []
        }

        // MARK: - NSFetchedResultsControllerDelegate conformance
        internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            let newPrescriptions = controller.fetchedObjects as? [Prescription] ?? []
            prescriptions = newPrescriptions
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文