使用 CoreData 如何配置获取请求以查找即将到来的生日?

Using CoreData how to configure a fetch request to find upcoming birthdays?

我正在使用 Xcode 7.3 和 Swift 2.2 以及 NSFetchedResultsController。是否可以创建使用谓词和排序描述符配置的所需提取请求来解决此问题?

给定一个具有 birthDate 属性的 Person 实体,我该如何配置对 return 即将到来的生日的提取请求?这是我尝试过的:

  1. 我创建了一个名为 birthDateThisYear 的临时属性,并将其配置为 return 这个人今年的生日。但是我发现你不能在 Core Data 的获取请求中使用瞬态属性。

  2. 我通过使用 birthDateDayNumberbirthDateMonthNumber 属性以及 birthDate 的自定义 setter 进行反规范化来尝试接受的答案 here但我无法弄清楚谓词。如果今天是 12 月 31 日怎么办?不知何故,它需要环绕以包括一月、二月和三月。

  3. 我读到可以用表达式和比较谓词来完成。但我想不出解决办法。有人成功了吗?

  4. 我认为创建一个名为 birthDateInYear2000 的非规范化属性是可行的,但同样,它也遇到了同样的重叠问题。

有什么想法吗?

建议:

  • 获取所有个生日。
  • 从现在开始将生日映射到下一个日期

    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 { [=11=].compare() == .OrderedAscending }
    
  • 现在 sortedNextBirthDays 包含所有即将到来的生日,并按升序排列。

在 Core Data 中,您可以获取包含生日和对象 ID(或全名)的字典形式的记录,创建一个临时结构,映射和排序项目并获取对象 ID 的人(或使用全名)——或者您甚至可以将逻辑应用于 NSManagedObject 数组

编辑

如果有关下一个生日的信息存储在持久存储中(假设它是MySQL-store),因为您不能应用排序描述符,包括指向瞬态或计算属性的键。

更新 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 属性 以及保持视图同步。

我的建议类似:

  • 获取全部customers/persons
  • 筛选出今天之前 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
    }
}

如果你想要,你可以在之后订购结果。

干杯

奥利弗