forked from C3-PRO/c3-pro-ios-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
HKHealthStore+Sampling.swift
121 lines (107 loc) · 5.08 KB
/
HKHealthStore+Sampling.swift
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
//
// HKExtensions.swift
// C3PRO
//
// Created by Pascal Pfiffner on 7/9/15.
// Copyright © 2015 Boston Children's Hospital. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import HealthKit
import SMART
/**
Extend `HKHealthStore` with methods that query the store for samples:
- `c3_latestSample(ofType:)`: retrieve the latest sample of the given type.
- `c3_samplesOfTypeBetween()`: retrieve all samples of a given type between two dates.
- `c3_summaryOfSamplesOfTypeBetween()`: return a summary of all samples of a given type. Use this to get an **aggregate count** of something
over a given period
*/
public extension HKHealthStore {
/**
Convenience method to retrieve the latest sample of a given type.
- parameter type: The type of samples to retrieve
- parameter callback: Callback to call when query finishes, comes back either with a quantity, an error or neither
*/
public func c3_latestSample(ofType type: HKQuantityTypeIdentifier, callback: @escaping ((HKQuantity?, Error?) -> Void)) {
c3_samplesOfTypeBetween(type, start: Date.distantPast , end: Date(), limit: 1) { results, error in
callback(results?.first?.quantity, error)
}
}
/**
Convenience method to retrieve samples from a given period. Orders by end date, descending. Don't use this to get a total over a given
period, use `c3_summaryOfSamplesOfTypeBetween()` (which is using `HKStatisticsCollectionQuery`).
- parameter typeIdentifier: The type of samples to retrieve
- parameter start: Start date
- parameter end: End date
- parameter limit: How many samples to retrieve at max
- parameter callback: Callback to call when query finishes, comes back either with an array of samples, an error or neither
*/
public func c3_samplesOfTypeBetween(_ typeIdentifier: HKQuantityTypeIdentifier, start: Date, end: Date, limit: Int, callback: @escaping ((_ results: [HKQuantitySample]?, _ error: Error?) -> Void)) {
guard let sampleType = HKSampleType.quantityType(forIdentifier: typeIdentifier) else {
callback(nil, C3Error.noSuchHKSampleType("\(typeIdentifier)"))
return
}
let period = HKQuery.predicateForSamples(withStart: start, end: end, options: HKQueryOptions())
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let query = HKSampleQuery(sampleType: sampleType, predicate: period, limit: limit, sortDescriptors: [sortDescriptor]) { sampleQuery, results, error in
if let err = error {
callback(nil, err)
}
else {
callback(results as? [HKQuantitySample], nil)
}
}
execute(query)
}
/**
Retrieve a quantity summed over a given period. Uses `HKStatisticsCollectionQuery`, the callback will be called on a background queue.
- parameter typeIdentifier: The type of samples to retrieve
- parameter start: Start date
- parameter end: End date
- parameter callback: Callback to call, on a background queue, when the query finishes, containing one HKQuantitySample spanning the
whole period or an error (or neither)
*/
public func c3_summaryOfSamplesOfTypeBetween(_ typeIdentifier: HKQuantityTypeIdentifier, start: Date, end: Date, callback: @escaping ((_ result: HKQuantitySample?, _ error: Error?) -> Void)) {
guard let sampleType = HKSampleType.quantityType(forIdentifier: typeIdentifier) else {
callback(nil, C3Error.noSuchHKSampleType("\(typeIdentifier)"))
return
}
// we create one interval for the whole period between start and end dates
let interval = Calendar.current.dateComponents([.day, .hour], from: start, to: end)
guard interval.day! + interval.hour! > 0 else {
c3_logIfDebug("Interval is too small: \(interval.day!)d \(interval.hour!)h from \(start) to \(end)")
callback(nil, C3Error.intervalTooSmall)
return
}
let period = HKQuery.predicateForSamples(withStart: start, end: end, options: HKQueryOptions())
let query = HKStatisticsCollectionQuery(quantityType: sampleType, quantitySamplePredicate: period, options: [.cumulativeSum], anchorDate: start, intervalComponents: interval)
query.initialResultsHandler = { sampleQuery, results, error in
if let error = error {
callback(nil, error)
}
else {
var sample: HKQuantitySample?
if let results = results {
results.enumerateStatistics(from: start, to: end) { statistics, stop in
if let sum = statistics.sumQuantity() {
sample = HKQuantitySample(type: sampleType, quantity: sum, start: start, end: end)
stop.pointee = true // we only expect one summary
}
}
}
callback(sample, nil)
}
}
execute(query)
}
}