forked from LoopKit/LoopKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSampleValue.swift
More file actions
131 lines (101 loc) · 3.67 KB
/
SampleValue.swift
File metadata and controls
131 lines (101 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//
// SampleValue.swift
// Naterade
//
// Created by Nathan Racklyeft on 1/24/16.
// Copyright © 2016 Nathan Racklyeft. All rights reserved.
//
import Foundation
import HealthKit
public protocol TimelineValue {
var startDate: Date { get }
var endDate: Date { get }
}
public extension TimelineValue {
var endDate: Date {
return startDate
}
}
public protocol SampleValue: TimelineValue {
var quantity: HKQuantity { get }
}
public extension Sequence where Element: TimelineValue {
/**
Returns the closest element in the sorted sequence prior to the specified date
- parameter date: The date to use in the search
- returns: The closest element, if any exist before the specified date
*/
func closestPrior(to date: Date) -> Iterator.Element? {
return elementsAdjacent(to: date).before
}
/// Returns the elements immediately before and after the specified date
///
/// - Parameter date: The date to use in the search
/// - Returns: The closest elements, if found
func elementsAdjacent(to date: Date) -> (before: Iterator.Element?, after: Iterator.Element?) {
var before: Iterator.Element?
var after: Iterator.Element?
for value in self {
if value.startDate <= date {
before = value
} else {
after = value
break
}
}
return (before, after)
}
/// Returns all elements inmmediately adjacent to the specified date
///
/// Use Sequence.elementsAdjacent(to:) if specific before/after references are necessary
///
/// - Parameter date: The date to use in the search
/// - Returns: The closest elements, if found
func allElementsAdjacent(to date: Date) -> [Iterator.Element] {
let (before, after) = elementsAdjacent(to: date)
return [before, after].compactMap({ $0 })
}
/**
Returns an array of elements filtered by the specified date range.
This behavior mimics HKQueryOptionNone, where the value must merely overlap the specified range,
not strictly exist inside of it.
- parameter startDate: The earliest date of elements to return
- parameter endDate: The latest date of elements to return
- returns: A new array of elements
*/
func filterDateRange(_ startDate: Date?, _ endDate: Date?) -> [Iterator.Element] {
return filter { (value) -> Bool in
if let startDate = startDate, value.endDate < startDate {
return false
}
if let endDate = endDate, value.startDate > endDate {
return false
}
return true
}
}
/**
Returns an array of elements filtered by the specified DateInterval.
This behavior mimics HKQueryOptionNone, where the value must merely overlap the specified range,
not strictly exist inside of it.
- parameter startDate: The earliest date of elements to return
- parameter endDate: The latest date of elements to return
- returns: A new array of elements
*/
func filterDateInterval(interval: DateInterval) -> [Iterator.Element] {
return filterDateRange(interval.start, interval.end)
}
}
public extension Sequence where Element: SampleValue {
func average(unit: HKUnit) -> HKQuantity? {
let (sum, count) = reduce(into: (sum: 0.0, count: 0)) { result, element in
result.0 += element.quantity.doubleValue(for: unit)
result.1 += 1
}
guard count > 0 else {
return nil
}
let average = sum / Double(count)
return HKQuantity(unit: unit, doubleValue: average)
}
}