I have two classes: Activity and Action. Activity is the parent class, Action are the child, this is a one to many relationship. In setting up a NSFetchedResultsController I would like to set a predicate based the properties of the child class. Here's an example...
fetchRequest.predicate = NSPredicate(format: "filter only activities that have actions which have their date property equalling today")
If I were to filter the activities using a for in loop, this is what it would look like...
for activity in activities
{
if activity.actions != nil
{
for action in activity.actions
{
if action.date == today
{
// add activity to filtered array
}
}
}
}
You can use a SUBQUERY
in your NSPredicate
. Here I used an NSArray
as a demonstration.
let today = Date()
let array: NSArray = [
Activity(actions: [
Action(date: today)
]),
Activity(actions: [
Action(date: Date(timeIntervalSince1970: 0))
])
]
// here is your predicate
let result = array.filtered(using: NSPredicate(format: "SUBQUERY(actions, $action, $action.date == %@) .@count > 0", today as CVarArg))
print(result.count) // 1
Note that I used ==
here to compare the dates, which means it will only match if the date is exactly equal to today
. If by "today" you meant "any time in the 24 hours", then you would have to use the answer suggested in this question:
func getLastAndNextMidnight(date: Date) -> (Date, Date) {
let dateComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
var oneDay = DateComponents()
oneDay.day = 1
let lastMidnight = Calendar.current.date(from: dateComponents)!
let nextMidnight = Calendar.current.date(byAdding: oneDay, to: lastMidnight)!
return (lastMidnight, nextMidnight)
}
let midnights = getLastAndNextMidnight(date: today)
let result = array.filtered(using: NSPredicate(format: "SUBQUERY(actions, $action, ($action.date >= %@) AND ($action.date <= %@)) .@count > 0", midnights.0 as CVarArg, midnights.1 as CVarArg))
print(result.count)