This might be a weird question because I don't fully understand how transient and the new derived properties work in Core Data.
So imagine I have a RegularClass
entity, which stores any class that repeats over time. A class can repeat for example every third day or every one week. This is how it looks in the data model:
(a RegularClass
belongs to a Schedule
entity, which in turn belongs to a Course
entity)
Now, if our class repeats every third day, we store the number 3 in the frequency
property, and a string "days"
in unit
property, which is then converted to an enum in Swift. A Schedule
, which every RegularClass
belongs to, has a startDate
property.
To check if a class happens at a given date, I came up with nothing better than calculating the difference in specified unit
between the startDate
and the given date, then taking a remainder between the difference and frequency, and if it's 0, than it's the date in which a class can occur.
var differenceComponent: Calendar.Component {
switch unitType {
case .weeks:
return .weekOfYear
case .days:
return .day
}
}
func getDifferenceFromDateComponents(_ dateComponents: DateComponents) -> Int? {
switch unitType {
case .weeks:
return dateComponents.weekOfYear
case .days:
return dateComponents.day
}
}
func dateIsInActiveState(_ date: Date) -> Bool {
if let startDate = schedule?.startDate {
let comps = Calendar.current.dateComponents([differenceComponent], from: startDate, to: date)
if let difference = getDifferenceFromDateComponents(comps) {
let remainder = Int64(difference) % frequency // that is the key!
return remainder == 0
}
}
return false
}
func containsDate(_ date: Date) -> Bool {
if dateIsInActiveState(date) {
if unitType == .days {
return true
}
let weekday = Calendar.current.component(.weekday, from: date)
return (weekdays?.allObjects as? [Weekday])?.contains(where: { $0.number == weekday }) ?? false
}
return false
}
Now, the thing is that this code works perfectly for courses that I've already got from a fetch request. But is there a way to pass a date parameter in a NSPredicate
to calculate this while request happens? Or do I have to fetch all the courses and then filter them out manually?
To solve this issue you could store your data as scalar types and then do simple arithmetic in your predicate. Rather than dates, use integers with a days-from-day-zero figure (or whatever minimum unit of time is necessary for these calculations). Store your repeat cycle as number-of-days.
Then you can use the calculation ((searchDate - startDate) mod repeatCycle) == 0
in your predicate to find matching classes.
As you have suggested, it might be sensible to denormalise your data for different search cases.