使用 CoreData 如何配置获取请求以查找即将到来的生日?
Posted
技术标签:
【中文标题】使用 CoreData 如何配置获取请求以查找即将到来的生日?【英文标题】:Using CoreData how to configure a fetch request to find upcoming birthdays? 【发布时间】:2016-04-15 03:45:09 【问题描述】:我正在使用带有NSFetchedResultsController
的 Xcode 7.3 和 Swift 2.2。是否可以创建配置了谓词和排序描述符的所需获取请求来解决此问题?
给定具有birthDate
属性的Person
实体,如何配置获取请求以返回即将到来的生日?这是我尝试过的:
我创建了一个名为 birthDateThisYear
的临时属性,并将其配置为返回此人今年的生日。但是我发现你不能在 Core Data 的获取请求中使用瞬态属性。
我尝试了接受的答案 here,方法是使用 birthDateDayNumber
和 birthDateMonthNumber
属性以及 birthDate
的自定义设置器进行非规范化,但我无法弄清楚谓词。如果今天是 12 月 31 日呢?不知何故,它需要环绕以包括一月、二月和三月。
我读到它可以通过表达式和比较谓词来完成。但我想不出解决办法。有人搞定了吗?
我认为创建一个名为birthDateInYear2000
的非规范化属性是可行的,但同样会遇到相同的重叠问题。
有什么想法吗?
【问题讨论】:
【参考方案1】:建议:
获取所有生日。将生日映射到从现在开始的下一个事件
let calendar = NSCalendar.currentCalendar()
let nextBirthDays = birthdays.map (date) -> NSDate in
let components = calendar.components([.Day, .Month], fromDate: date)
return calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
对日期进行排序
let sortedNextBirthDays = nextBirthDays.sort $0.compare($1) == .OrderedAscending
现在sortedNextBirthDays
包含所有即将到来的生日升序排列。
在 Core Data 中,您可以将记录作为带有生日和 objectID(或全名)的字典,创建一个临时结构,映射和排序项目并获取 objectID 的人(或使用全名) - 或者您甚至可以将逻辑应用于NSManagedObject
数组
编辑
使用NSFetchedResultsController
,您可以仅在有关下一个生日的信息存储在持久存储中(假设它是 mysql 存储)时对表视图进行排序,因为您无法申请排序描述符,包括指向瞬态或计算属性的键。
更新nextBirthDate
属性的最佳位置是在创建视图控制器的NSFetchedResultsController
实例之前。
Person
中创建(可选)属性nextBirthDate
创建NSDate
的扩展来计算从现在开始的下一次出现的日期
extension NSDate
var nextOccurrence : NSDate
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day, .Month], fromDate: self)
return calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
在初始化NSFetchResultsController
实例的闭包中添加代码更新每条记录的nextBirthDate
属性
lazy var fetchedResultsController: NSFetchedResultsController =
let fetchRequest = NSFetchRequest(entityName: "Person")
do
let people = try self.managedObjectContext.executeFetchRequest(fetchRequest) as! [Person]
people.forEach( (person) in
let nextBirthDate = person.birthDate.nextOccurrence
if person.nextBirthDate == nil || person.nextBirthDate! != nextBirthDate
person.nextBirthDate = nextBirthDate
)
if self.managedObjectContext.hasChanges
try self.managedObjectContext.save()
catch
print(error)
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptors = [NSSortDescriptor(key:"nextBirthDate", ascending: true)]
fetchRequest.sortDescriptors = sortDescriptors
...
如果视图控制器可以编辑birthDate
属性,请不要忘记更新nextBirthDate
属性以保持视图同步。
【讨论】:
看起来很有希望!我忘了说我正在使用NSFetchedResultsController
。你认为你的答案可以适应吗?似乎并非如此。它需要传递一个配置好的NSFetchRequest
。
我用NSFetchedResultsController
更新了建议解决方案的答案。唯一的缺点是nextBirthDate
属性必须是持久的。
好主意!我会试试看。这可能不适用于大型数据集,但此应用程序的 Person 实体数量很少。可能不超过 25 个。
我不认为存在性能问题。 nextBirthDate
属性仅在第一次运行时更改,并且如果新值与保存的值不同。无论如何,从数据库到数据库总是只有一个获取和一个保存任务。基本上考虑将出生日期存储在中午而不是午夜,以避免潜在的夏令时更改问题。顺便说一句:闰年 2 月 29 日的生日计算为非闰年的 3 月 1 日。要在 2 月 28 日将 .MatchNextTime
更改为 .MatchPreviousTimePreservingSmallerUnits
在 NSDate
扩展中。
对我的情况做了一些小的改变,这就像一个魅力。谢谢!【参考方案2】:
我的建议类似:
获取所有客户/人员 过滤范围为今天之前 3 天和之后 3 天的所有生日像下面这样使用闭包:
func fetchBirthdayList()
// this example considers all birthdays that are like
// |---TODAY---|
// Each hyphen represents a day
let date = Date()
let timeSpan = 3
let cal = Calendar.current
nextBirthDays = self.list.filter (customer) -> Bool in
if let birthDate = customer.birthDate
let difference = cal.dateComponents([.day,.year], from: birthDate as! Date, to: date)
return (( (difference.day! <= timeSpan) || ((365 - difference.day!) <= timeSpan) ) && difference.day! >= 0)
return false
如果需要,您可以在之后订购结果。
干杯
奥利弗
【讨论】:
以上是关于使用 CoreData 如何配置获取请求以查找即将到来的生日?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI CoreData - 如何更新获取请求和列表