SampleValue.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. //
  2. // SampleValue.swift
  3. // Naterade
  4. //
  5. // Created by Nathan Racklyeft on 1/24/16.
  6. // Copyright © 2016 Nathan Racklyeft. All rights reserved.
  7. //
  8. import Foundation
  9. import HealthKit
  10. public protocol TimelineValue {
  11. var startDate: Date { get }
  12. var endDate: Date { get }
  13. }
  14. public extension TimelineValue {
  15. var endDate: Date {
  16. return startDate
  17. }
  18. }
  19. public protocol SampleValue: TimelineValue {
  20. var quantity: HKQuantity { get }
  21. }
  22. public extension Sequence where Element: TimelineValue {
  23. /**
  24. Returns the closest element in the sorted sequence prior to the specified date
  25. - parameter date: The date to use in the search
  26. - returns: The closest element, if any exist before the specified date
  27. */
  28. func closestPrior(to date: Date) -> Iterator.Element? {
  29. return elementsAdjacent(to: date).before
  30. }
  31. /// Returns the elements immediately before and after the specified date
  32. ///
  33. /// - Parameter date: The date to use in the search
  34. /// - Returns: The closest elements, if found
  35. func elementsAdjacent(to date: Date) -> (before: Iterator.Element?, after: Iterator.Element?) {
  36. var before: Iterator.Element?
  37. var after: Iterator.Element?
  38. for value in self {
  39. if value.startDate <= date {
  40. before = value
  41. } else {
  42. after = value
  43. break
  44. }
  45. }
  46. return (before, after)
  47. }
  48. /// Returns all elements inmmediately adjacent to the specified date
  49. ///
  50. /// Use Sequence.elementsAdjacent(to:) if specific before/after references are necessary
  51. ///
  52. /// - Parameter date: The date to use in the search
  53. /// - Returns: The closest elements, if found
  54. func allElementsAdjacent(to date: Date) -> [Iterator.Element] {
  55. let (before, after) = elementsAdjacent(to: date)
  56. return [before, after].compactMap({ $0 })
  57. }
  58. /**
  59. Returns an array of elements filtered by the specified date range.
  60. This behavior mimics HKQueryOptionNone, where the value must merely overlap the specified range,
  61. not strictly exist inside of it.
  62. - parameter startDate: The earliest date of elements to return
  63. - parameter endDate: The latest date of elements to return
  64. - returns: A new array of elements
  65. */
  66. func filterDateRange(_ startDate: Date?, _ endDate: Date?) -> [Iterator.Element] {
  67. return filter { (value) -> Bool in
  68. if let startDate = startDate, value.endDate < startDate {
  69. return false
  70. }
  71. if let endDate = endDate, value.startDate > endDate {
  72. return false
  73. }
  74. return true
  75. }
  76. }
  77. }
  78. public extension Sequence where Element: SampleValue {
  79. func average(unit: HKUnit) -> HKQuantity? {
  80. let (sum, count) = reduce(into: (sum: 0.0, count: 0)) { result, element in
  81. result.0 += element.quantity.doubleValue(for: unit)
  82. result.1 += 1
  83. }
  84. guard count > 0 else {
  85. return nil
  86. }
  87. let average = sum / Double(count)
  88. return HKQuantity(unit: unit, doubleValue: average)
  89. }
  90. }