I'm storing a list of birthdays in Core Data, and I'm using an NSFetchedResultsController
to return all the birthdays in Core Data. The main issue I'm having is with the year. I want to show birthdays that occur one month from now. But as you know a person could be born on October 18, 2005 but comparing it with a date a month from now doesn't work, because assuming today is October 15, 2016 I would be comparing November 1, 2016 with October 18, 2005 so my app would think the birthday doesn't happen a month from now. How do I check if a birthday occurs in the next month using NSPredicate?
Here is some code I'm using right now without success:
class BirthdayFetchedResultsController: NSFetchedResultsController<Birthday>, NSFetchedResultsControllerDelegate {
private let tableView: UITableView
init(managedObjectContext: NSManagedObjectContext, tableView: UITableView) {
self.tableView = tableView
let request: NSFetchRequest<Birthday> = Birthday.fetchRequest()
let dateSortDescriptor = NSSortDescriptor(key: "date", ascending: true)
request.sortDescriptors = [dateSortDescriptor]
let startOfDay = Calendar.current.startOfDay(for: Date()) as NSDate
// FIXME: The year matters here when it shouldn't.
let predicate = NSPredicate(format: "date >= %@", startOfDay)
request.predicate = predicate
// TODO: Add section name key path for sorting the birthdays into sections
super.init(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
self.delegate = self
tryFetch()
}
func tryFetch() {
do {
try performFetch()
} catch let error as NSError {
// TODO: Implement Error Handling
print("Unresolved error: \(error), \(error.userInfo)")
}
}
}
UPDATE: Now, I have an idea of how to deal with this issue. Either the month has to be less than or equal to the current month plus one, it can be less than the current month minus eleven or the month can be the same and the date has to be greater. I tried to build a predicate for this but it doesn't seem to be working:
let calendar = Calendar.current
let now = Date()
let month = calendar.component(.month, from: now)
let date = calendar.component(.day, from: now)
let predicate = NSPredicate(format: "((month <= %@) or (month <= %@)) or ((month == %@) AND (date >= %@))", argumentArray: [month + 2, month - 10, month, date])
request.predicate = predicate
答案 0 :(得分:3)
如果您正在寻找下周或月份发生的重复事件的所有实例,那么“日期”属性不是一个好的选择。获得您需要的结果充其量是非常尴尬的。核心数据中的日期对应于Swift Date
,它代表一个时刻,并且不存储月份或日期等详细信息。
最好以您需要的形式存储这些详细信息,例如使用月和日的整数值。这使查找相当简单,因为您有字段存储您需要用作过滤器的值。没有什么好方法可以根据需要使用日期属性。一个糟糕的方法是计算过去N年中每个过去一年的月间隔(对于你选择的任何N)并构建一个结合所有这些的大量谓词。我不推荐它。
您还必须处理12月的特殊情况,当前日期可能是12/18,生日是01/01。在那种情况下,它将不会检测到生日即将到来,因为12> 1。
这是一个示例谓词:
// Month Conditions
let monthCondition1 = NSPredicate(format: "month <= %i", month + 1)
let monthCondition2 = NSPredicate(format: "month > %i", month)
let monthConditions = NSCompoundPredicate(andPredicateWithSubpredicates: [monthCondition1, monthCondition2])
// Date Conditions
let dateCondition1 = NSPredicate(format: "month == %i", month)
let dateCondition2 = NSPredicate(format: "date >= %i", date)
let dateConditions = NSCompoundPredicate(andPredicateWithSubpredicates: [dateCondition1, dateCondition2])
// Special Month Condition - Example: If currentDate is 12/31 and the birthday is 01/01 current month is greater than birthday but we still show it.
var predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [monthConditions, dateConditions])
if month == 12 {
let specialMonthCondition1 = NSPredicate(format: "month == %i", 1)
predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicate, specialMonthCondition1])
}
request.predicate = predicate