CoreData возвращает пустые данные - PullRequest
1 голос
/ 15 марта 2019

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

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

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)

    }

}

// удалить данные из основных данных

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

    })

}

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

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

enter image description here

1 Ответ

1 голос
/ 31 марта 2019

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

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

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

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

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

...