CoreData не может инициализировать управляемый объект при доступе через пользовательское действие UserNotification - PullRequest
0 голосов
/ 22 февраля 2019

Я пытался решить эту проблему в течение некоторого времени, но безрезультатно.
При доступе к управляемому объекту из настраиваемого действия UserNotification, а затем при попытке сохранить изменения в этом объекте я получаю следующее сообщение:

[ошибка] ошибка: CoreData: ошибка: не удалось вызвать назначенный инициализатор для класса NSManagedObject «NSManagedObject».

По сути, установка выглядит следующим образом:
1. Пользователь получает уведомление
2. Выбирает пользовательское действие
3. Из информации в уведомлении делегат Центра UserNotification Centerизвлекает URI объекта, а затем извлекает его из постоянного хранилища
4. После завершения и приведения типа делегат вызывает соответствующий метод для объекта
5. После возврата метода делегат пытается сохранить контекст, и этогде появляется ошибка.

Вот соответствующий код:

// - UNUserNotification Centre delegate
extension HPBUserNotificationsHandler: UNUserNotificationCenterDelegate {    
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    guard let object = getAssociatedObject(id: response.notification.request.identifier) else { return }

    switch response.actionIdentifier {
    case UNNotificationDismissActionIdentifier:
        ....
    case UNNotificationDefaultActionIdentifier:
        ....
    case HPBReminderAction.take.rawValue:
        // get intake object
        guard let reminder = object as? HPBIntake else { return }
        reminder.take(at: Date()) 
        try! dataController.saveContext() // here is when the error is raised
    default:
        break
    }

    completionHandler()
}

Функция для извлечения объекта из постоянного хранилища:

func getAssociatedObject(id: String) -> NSManagedObject? {
    guard let psc = dataController.managedObjectContext.persistentStoreCoordinator else { return nil }
    guard let objURL = URL(string: id) else { return nil }
    guard let moid = psc.managedObjectID(forURIRepresentation: objURL) else { return nil }
    return dataController.managedObjectContext.object(with: moid)
}

Если я внесу те же изменения непосредственно в этот объектв приложении - все работает.Поэтому я предполагаю, что проблема заключается в получении объекта из настраиваемого действия в уведомлении пользователя.Но я не могу понять, в чем проблема.

Вот дополнительная информация.Когда я проверяю объект reminder прямо перед вызовом функции take(on:), он выдает ошибку:

Home_Pillbox.HPBIntake: 0x7fb1a9074e90 (entity: Intake; id: 0x7fb1a9069e50 x-coredata: /// Intake / tAC4BBCD4-B128-4C6F-8E1B-2EE7D4EDBCB34; data: fault)

Конечно, при вызове функции выдается ошибка, но объект инициализируется неправильно и вместо этого заполняетсявсе свойства как nil:

Home_Pillbox.HPBIntake: 0x7fb1a9074e90 (сущность: входная;{дозировка = 0; идентификатор = ноль; localNotification = ноль; журнал = ноль; прием пищи = 0; medName = ноль; уведомлениеRequest = ноль; profileName = ноль; расписание = ноль; статус = 1; лечение = ноль; единица измерения = 0; userNotes= nil;})

Поэтому, когда контекст пытается сохранить его, он не может этого сделать, так как свойства имеют значение nil, что недопустимо в модели данных.Что также беспокоит меня, так это то, что в ошибке упоминается обозначенный инициализатор на NSManagedObject вместо имени подкласса HPBIntake, даже если объект четко введен правильно.

Любая помощь будет высоко оценена.

РЕДАКТИРОВАТЬ: Вот реализация функции saveContext() в DataController:

    func saveContext() throws {
        if managedObjectContext.hasChanges {
            do {
                try managedObjectContext.save()
            } catch let syserr as NSError {
                throw syserror
            }
        }
    }

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Еще одна идея: поскольку вы получаете ошибку инициализатора, мне кажется, что объект, идентификатор которого вы передаете через уведомление, еще не инициализируется при попытке сохранить его.
В эта ссылка Я обнаружил следующее:

объект (с :) выдает исключение, если не удается найти запись для идентификатора объекта, который он получает.Например, если приложение удалило запись, соответствующую идентификатору объекта, Core Data не сможет передать вашему приложению соответствующую запись.Результат - исключение.
Существующий метод (с :) ведет себя аналогичным образом.Основное отличие состоит в том, что метод выдает ошибку, если не может извлечь управляемый объект, соответствующий идентификатору объекта.

Поэтому я предлагаю заменить в getAssociatedObject(id: String) вызов object(with: moid) на existingObject(with: moid).Если это приводит к ошибке, вы знаете, что связанный объект еще не существует или больше не существует.
Если это разрешено вашим приложением, вы должны были инициализировать его назначенным инициализатором

init(entity entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?)  

доВы пытаетесь сохранить его.

РЕДАКТИРОВАТЬ:

В этот ответ , вы можете найти дополнительные предложения по отладке вашей основной обработки данных.

0 голосов
/ 24 февраля 2019

Просто идея: вы сказали

Если я внесу те же изменения в этот объект непосредственно в приложении - все работает.

Я полагаю, вы делаете эти измененияв основной теме.

Вы проверяли, выполняется ли userNotificationCenter(_:didReceive:withCompletionHandler:) также в основном потоке?Если нет, у вас могут возникнуть проблемы, поскольку данные ядра ожидают выполнения только в одном потоке.В этом случае вы можете попытаться выполнить тело этой функции с помощью

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
  DispatchQueue.main.async {
    // Your code here
  }
}

В вашей схеме также следует установить аргумент запуска -com.apple.CoreData.ConcurrencyDebug 1 вместе с точками останова исключения:
enter image description here Тогда ваше приложение остановится, когда произойдет нарушение многопоточности основных данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...