CoreData возвращает пустые данные

#ios #swift #core-data #nsfetchrequest

#iOS #swift #core-data #nsfetchrequest

Вопрос:

CoreData возвращает пустые данные, когда их не должно быть, даже если вы удалите приложение и переустановите его и сделаете запрос к Core Data, context.fetch вернет данные

получить все данные в Core Data

 func getMyLoadBook(){
    words.removeAll()

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let context = appDelegate.persistentContainer.viewContext
    let fetchRequest:NSFetchRequest<Favorite> = Favorite.fetchRequest()
    fetchRequest.returnsObjectsAsFaults = false

    do {
        let result = try! context.fetch(fetchRequest)
        print(result)
        if result.isEmpty {
            emptyBookMark()
            return
        } else {
            tableView.isHidden = false
        }

        for data in result as [NSManagedObject] {
            if let _ = data.value(forKey: "word"){
                let initData = Words(word: (data.value(forKey: "word") as? [String]) ?? [""], wordDesc: (data.value(forKey: "wordDesc") as? [String]) ?? nil, translation: (data.value(forKey: "translation") as? [String]) ?? [""], translDesc: (data.value(forKey: "translDesc") as? [String]) ?? nil)
                words.append(initData)
            }
        }

    }

    tableView.reloadData()

}
  

У меня есть эти функции, но они не вызываются, когда я получаю данные из CoreData

// создает путь и проверяет наличие элемента

 static func coreDataResult(data: [[String?]?]?, completion: @escaping (NSFetchRequest<NSFetchRequestResult>, Favorite?, NSManagedObjectContext) -> ()){

    guard let w = data?.first, let word = w, let t = data?.last, let transl = t else { return }

    DispatchQueue.main.async {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let context = appDelegate.persistentContainer.viewContext
        guard let entity = NSEntityDescription.entity(forEntityName: "Favorite", in: context) else { return }
        guard let taskObject = NSManagedObject(entity: entity, insertInto: context) as? Favorite else { return }

        let predicate = NSPredicate(format: "word == %@", word)
        let predicate2 = NSPredicate(format: "translation == %@", transl)
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Favorite")
        let andPredicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, predicate2])

        fetchRequest.predicate = andPredicate
        completion(fetchRequest, taskObject, context)

    }

}
  

// удалить данные из Core Data

 static func deleteFromCoreData(data: [[String?]?]?){

    coreDataResult(data: data, completion: { (result, taskObject, context) in

        do {
            let fetchedEntities = try context.fetch(result) as! [Favorite]
            if let entityToDelete = fetchedEntities.first {
                context.delete(entityToDelete)
            }
            do {
                try context.save()
                if let data = getDataFromContext(result:fetchedEntities){
                    Analytics.logEvent("RemovedFavorite", parameters: ["word": data.0, "translation": data.1])
                    YMMYandexMetrica.reportEvent("RemovedFavorite", parameters: ["word": data.0, "translation": data.1], onFailure: nil)

                }
            } catch {
                print(error)
            }
        } catch { print(error) }

    })

}
  

// добавить данные в Core Data

 static func saveWithModelToCoreData(_ words: Words){

    DispatchQueue.main.async {

        coreDataResult(data: [words.word, words.translation], completion: { (result, taskObject, context) in
            do {
                let fetchedEntities = try context.fetch(result) as! [Favorite]
                if let _ = fetchedEntities.first?.word {
                    print("the element already have in coreData")
                } else {

                    taskObject?.setValue(words.word, forKey: "word")
                    taskObject?.setValue(words.translation, forKey: "translation")
                    taskObject?.setValue(words.descript, forKey: "wordDesc")
                    taskObject?.setValue(words.translDesc, forKey: "translDesc")

                    do {
                        try context.save()
                    } catch {
                        print(error)
                    }
                }
            } catch {
                print(error)
            }
        })

    }

}
  

вот какой результат возвращает

 [<Favorite: 0x283478500> (entity: Favorite; id: 0x281306ee0 <x-coredata:///Favorite/t722DD7F9-8DD7-4AC4-AA20-02324AB1B08713> ; data: {
translDesc = nil;
translation = nil;
word = nil;
wordDesc = nil;
})
  

введите описание изображения здесь

Комментарии:

1. вы пробовали заменить let fetchRequest:NSFetchRequest<Favorite> = Favorite.fetchRequest() на let fetchRequest:NSFetchRequest< NSFetchRequestResult> = Favorite.fetchRequest() ?

2. Не связано, но вы используете слишком много вопросительных знаков. [[String?]?]? это весело. Например, почему параметр в saveWithModelToCoreData необязательный? Когда вы собираетесь вызвать этот метод, вы действительно хотите что-то сохранить.

3. @NSCoder Я так и сделал

4. у @vadian привычка ставить необязательные

5. Если я вас правильно понимаю, вы говорите, что при новой установке вашего приложения, а затем извлечении данных из core data, вам возвращается объект (в вашем вопросе: id: 0x281306ee0), хотя вы не сохраняли этот объект раньше. Это невозможно, потому что при деинсталляции приложения все постоянные данные удаляются. Просто дважды проверьте, что вы удалили свое приложение, и, если ошибка сохраняется, удалите комментарии ко всему, что сохраняется в core data, и повторите попытку.

Ответ №1:

Кажется, что вы используете простую настройку core-data, где все чтение и запись выполняются в основном потоке в viewContext. Эта настройка подходит для простого приложения, где вы не ожидаете выполнять массовый импорт или иметь огромное количество объектов. Это должно упростить множество многопоточных проблем, поэтому я немного смущен, почему у вас такая сложная настройка с обратными вызовами и DispatchQueue.main.async когда все должно просто выполняться в основном потоке. (Возможно, вы планируете будущее с более сложной настройкой?).

В любом случае, одним из последствий этого является то, что любые изменения в viewContext будут отображаться в вашем приложении в течение всего срока службы приложения, даже если вы не вызываете save . Это потому, что существует единственный контекст — поэтому, даже если он не сохранен, он все равно был изменен.

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

Если приложение завершит работу сразу после этого, вы не увидите его при следующем запуске. Но если save вызывается в любое время после этого, то пустой объект также будет сохранен.

Я бы посоветовал не создавать объекты, если вы уже не знаете, что они вам нужны. Я бы провел рефакторинг, чтобы была единственная функция, которая проверяет наличие дубликатов, а затем создает и устанавливает или ничего не делает. Как есть, я не вижу значения двух разных методов.

Комментарии:

1. Я принял к сведению ваш совет, проблема заключалась в guard let taskObject = NSManagedObject (entity: insertInto: context) as? Favorite else { return }