Как использовать предикат для управляемых объектов "один ко многим" в Core Data - PullRequest
0 голосов
/ 03 августа 2020

В настоящее время у меня есть два управляемых объекта для основных данных, которые имеют отношение «один ко многим».

Цель

extension Goal {

    @nonobjc public class func createFetchRequest() -> NSFetchRequest<Goal> {
        return NSFetchRequest<Goal>(entityName: "Goal")
    }

    @NSManaged public var title: String
    @NSManaged public var date: Date
    @NSManaged public var progress: NSSet?

}

Прогресс

extension Progress {

    @nonobjc public class func createFetchRequest() -> NSFetchRequest<Progress> {
        return NSFetchRequest<Progress>(entityName: "Progress")
    }

    @NSManaged public var date: Date
    @NSManaged public var comment: String?
    @NSManaged public var goal: Goal

}

Для каждой цели вы можете иметь несколько Progress объектов. Проблема в том, что когда я запрашиваю выборку для Progress с конкретным Goal в качестве предиката, ничего не возвращается. У меня есть подозрение, что я неправильно использую предикат.

Вот как я их запрашиваю.

  1. Сначала я беру Goal для контроллера табличного представления:
var fetchedResultsController: NSFetchedResultsController<Goal>!

if fetchedResultsController == nil {
    let request = Goal.createFetchRequest()
    let sort = NSSortDescriptor(key: "date", ascending: false)
    request.sortDescriptors = [sort]
    request.fetchBatchSize = 20
    
    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.context, sectionNameKeyPath: "title", cacheName: nil)
    fetchedResultsController.delegate = self
}

fetchedResultsController.fetchRequest.predicate = goalPredicate

do {
    try fetchedResultsController.performFetch()
} catch {
    print("Fetch failed")
}
И передать результат на следующий экран, Detail контроллер просмотра:
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
    vc.goal = fetchedResultsController.object(at: indexPath)
    navigationController?.pushViewController(vc, animated: true)
}
Наконец, я получаю Progress, используя Goal в качестве предиката из Detail контроллера представления:
var goal: Goal!
let progressRequest = Progress.createFetchRequest()
progressRequest.predicate = NSPredicate(format: "goal == %@", goal)

if let progress = try? self.context.fetch(progressRequest) {
    print("progress: \(progress)")

    if progress.count > 0 {
        fetchedResult = progress[0]
        print("fetchedResult: \(fetchedResult)")
    }
}

Goal возвращается правильно, но я ничего не получаю. для Progress. Я пробовал:

progressRequest.predicate = NSPredicate(format: "goal.title == %@", goal.title)

или

progressRequest.predicate = NSPredicate(format: "ANY goal == %@", goal)

, но результат тот же.

Ниже показано, как я установил связь:

// input for Progress from the user
let progress = Progress(context: self.context)
progress.date = Date()
progress.comment = commentTextView.text

// fetch the related Goal
var goalForProgress: Goal!
let goalRequest = Goal.createFetchRequest()
goalRequest.predicate = NSPredicate(format: "title == %@", titleLabel.text!)

if let goal = try? self.context.fetch(goalRequest) {
    if goal.count > 0 {
        goalForProgress = goal[0]
    }
}

// establish the relationship between Goal and Progress
goalForProgress.progress.insert(progress)

// save
if self.context.hasChanges {
    do {
        try self.context.save()
    } catch {
        print("An error occurred while saving: \(error.localizedDescription)")
    }
}

Ответы [ 2 ]

1 голос
/ 03 августа 2020

На самом деле вам не нужно повторно загружать данные. Вы можете получить прогресс из отношения

  • Объявить progress как родное Set

    @NSManaged public var progress: Set<Progress>
    
  • В DetailViewController удалить получить код в viewDidLoad и объявить

    var progress: Progress!
    
  • В первом контроллере просмотра отфильтровать прогресс

    let goal = fetchedResultsController.object(at: indexPath)
    if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
       let progress = goal.progress.first(where: {$0.goal.title == goal.title}) {
        vc.progress = progress
        navigationController?.pushViewController(vc, animated: true)
    }
    

И подумать о названии отношение ко многим во множественном числе (progresses)

0 голосов
/ 04 августа 2020

Я понял, что это связано с ошибкой Core Data Fault, когда Core Data лениво загружает данные, и если вы явно не обращаетесь к данным, значение не будет отображаться.

let goal = fetchedResultsController.object(at: indexPath)
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
    let progress = goal.progress.first(where: {$0.goal.title == goal.title}) {
    vc.goalTitle = goal.title
    vc.date = progress.date
    
    if let comment = progress.comment {
        vc.comment = comment
    }
    
    navigationController?.pushViewController(vc, animated: true)
}

или установите returnsObjectsAsFaults на false.

Вот хорошая артикул на топи c.

...