如何在 Core Data 中为一对多托管对象使用谓词
How to use a predicate for one-to-many managed objects in Core Data
我目前有两个具有一对多关系的 Core Data 托管对象。
目标
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
对象。问题是当我请求以特定 Goal
作为谓词获取 Progress
时,没有返回任何内容。我怀疑我没有正确使用谓词。
这就是我的要求。
- 首先,我为 table 视图控制器获取
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)
}
- 最后,我使用
Goal
作为谓词从 Detail
视图控制器获取 Progress
:
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)")
}
}
实际上您不需要重新获取数据。可以从关系中获取进度
声明 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: {[=12=].goal.title == goal.title}) {
vc.progress = progress
navigationController?.pushViewController(vc, animated: true)
}
并考虑以复数形式命名 to-many 关系 (progresses
)
我发现这是由于 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: {[=10=].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
。
这里有一个很好的article主题。
我目前有两个具有一对多关系的 Core Data 托管对象。
目标
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
对象。问题是当我请求以特定 Goal
作为谓词获取 Progress
时,没有返回任何内容。我怀疑我没有正确使用谓词。
这就是我的要求。
- 首先,我为 table 视图控制器获取
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)
}
- 最后,我使用
Goal
作为谓词从Detail
视图控制器获取Progress
:
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)")
}
}
实际上您不需要重新获取数据。可以从关系中获取进度
声明
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: {[=12=].goal.title == goal.title}) { vc.progress = progress navigationController?.pushViewController(vc, animated: true) }
并考虑以复数形式命名 to-many 关系 (progresses
)
我发现这是由于 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: {[=10=].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
。
这里有一个很好的article主题。