Created
March 8, 2017 04:45
-
-
Save kalm42/b7d4a14976e0167f4612c2ff406a2ff5 to your computer and use it in GitHub Desktop.
Health Store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class HealthData { | |
//MARK: - Class Properties | |
private let healthStore = HKHealthStore() | |
private let queue = DispatchQueue(label: "com.kalm42.your-fat.hd", attributes: .concurrent) | |
private let group = DispatchGroup() | |
var perferredUnitOfMeasurement: UnitOfMeasurement = .lb | |
var isFinishedLoading: Bool = false | |
//HealthKit variables | |
private var hkUnitOfMeasurement = HKUnit.pound() | |
private var bodyMassQuantityType: HKQuantityType? = HKObjectType.quantityType( | |
forIdentifier: HKQuantityTypeIdentifier.bodyMass | |
) | |
private var bodyFatPercentageQuantityType: HKQuantityType? = HKObjectType.quantityType( | |
forIdentifier: HKQuantityTypeIdentifier.bodyFatPercentage | |
) | |
private var bodyMassSampleType: HKSampleType? = HKObjectType.quantityType( | |
forIdentifier: HKQuantityTypeIdentifier.bodyMass | |
) | |
private var bodyFatPercentageSampleType: HKSampleType? = HKObjectType.quantityType( | |
forIdentifier: HKQuantityTypeIdentifier.bodyFatPercentage | |
) | |
// | |
enum HKHealthStoreError: Error { | |
case HealthDataNotAvailable | |
case RequestForAccessNotDisplayed | |
} | |
//init | |
init() { | |
if HKHealthStore.isHealthDataAvailable() { | |
do { | |
try requestAccess() | |
} catch { | |
print("Request for access failed to display to the user and was not previously displayed.") | |
} | |
} else { | |
fatalError("The health store is not available.") | |
} | |
getPreferredUnits() | |
queue.async { | |
if let bodyMassSampleType = self.bodyMassSampleType { | |
self.getSamplesFromHealthKit(for: bodyMassSampleType) | |
} | |
} | |
queue.async { | |
if let bodyFatPercentageSampleType = self.bodyFatPercentageSampleType { | |
self.getSamplesFromHealthKit(for: bodyFatPercentageSampleType) | |
} | |
} | |
group.notify(queue: .main){ | |
self.isFinishedLoading = true | |
} | |
} | |
//MARK: - Private Functions | |
private func requestAccess() throws { | |
if let bodyMassSampleType = bodyMassSampleType, | |
let bodyFatPercentageSampleType = bodyFatPercentageSampleType, | |
let bodyMassQuantityType = bodyMassQuantityType, | |
let bodyFatPercentageQuantityType = bodyFatPercentageQuantityType | |
{ | |
let dataTypesToWrite: Set = [bodyMassSampleType, bodyFatPercentageSampleType] | |
let dataTypesToRead: Set = [bodyMassQuantityType, bodyFatPercentageQuantityType] | |
// Request auth | |
healthStore.requestAuthorization(toShare: dataTypesToWrite, read: dataTypesToRead, completion: successOrError) | |
} else { | |
throw HKHealthStoreError.RequestForAccessNotDisplayed | |
} | |
} | |
private func getPreferredUnits() { | |
if let bodyMass = bodyMassQuantityType { | |
let bodyMassSet: Set<HKQuantityType> = [bodyMass] | |
healthStore.preferredUnits(for: bodyMassSet, completion: unitDictionaryOrError) | |
} | |
} | |
private func successOrError(success: Bool, AuthorizationError: Error?) -> Void { | |
if success { | |
print("Success.") | |
} else { | |
print("Failure.") | |
} | |
if let error = AuthorizationError { | |
print(error) | |
} | |
} | |
// sets the perferredUnitOfMeasurement | |
private func unitDictionaryOrError (unitForQuantityTypeDictionary: [HKQuantityType: HKUnit], preferredUnitError: Error?) -> Void { | |
for (quantityType, unit) in unitForQuantityTypeDictionary { | |
if quantityType == self.bodyMassQuantityType, let preferredUnit = UnitOfMeasurement(rawValue: unit.unitString) { | |
self.hkUnitOfMeasurement = unit | |
self.perferredUnitOfMeasurement = preferredUnit | |
} | |
} | |
} | |
private func getSamplesFromHealthKit(for sampleType: HKSampleType){ | |
//1. Set the SampleType to find | |
// Its a global var bodyFatPercentageSample | |
//2. Write the query, date range, and all other options for the query | |
//2a. Dates | |
let endDate = Date() //now | |
let startDate = Date(timeIntervalSinceNow: -TimeLengthInSeconds.oneYear.rawValue) | |
//2b. Query Options | |
let queryOptions = HKQueryOptions() | |
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: queryOptions) | |
//3. How many results can be returned. | |
let resultLimit = Int(HKObjectQueryNoLimit) | |
//4. Run this bitch | |
let query = HKSampleQuery( | |
sampleType: sampleType, predicate: predicate, limit: resultLimit, sortDescriptors: nil, resultsHandler: convertHealthKitSample | |
) | |
self.healthStore.execute(query) | |
} | |
private func convertHealthKitSample(query: HKSampleQuery, samples: [HKSample]?, error: Error?) -> Void { | |
queue.async { | |
self.group.enter() | |
guard let samples = samples as? [HKQuantitySample] else { | |
fatalError("Failed to downcast health kit samples to hk quantity samples") | |
} | |
for sample in samples { | |
let quantity = sample.quantity | |
var bodyFatSamples: [BodyFatPercentageSample]? = nil | |
var bodyMassSamples: [BodyMassSample]? = nil | |
if quantity.is(compatibleWith: HKUnit.percent()) { | |
//is body fat percentage | |
let value = quantity.doubleValue(for: HKUnit.percent()) | |
let date = sample.endDate | |
let bodyFat = BodyFatPercentageSample(date: date, value: value) | |
bodyFatSamples?.insert(bodyFat, at: 0) | |
} else { | |
//is body mass | |
let value = quantity.doubleValue(for: self.hkUnitOfMeasurement) | |
let date = sample.endDate | |
let bodyMass = BodyMassSample(date: date, value: value, unit: self.perferredUnitOfMeasurement) | |
bodyMassSamples?.insert(bodyMass, at: 0) | |
} | |
} | |
} | |
} | |
//MARK: - Public Methods | |
func record(bodyMass: Double, bodyFatPercentage: Double, at date: Date){ | |
var samples: [HKObject] = [] | |
// Add the body mass to the array. | |
if let bodyMassQuantityType = self.bodyMassQuantityType { | |
let bodyMassQuantity = HKQuantity(unit: hkUnitOfMeasurement, doubleValue: bodyMass) | |
samples.append( | |
HKQuantitySample(type: bodyMassQuantityType, quantity: bodyMassQuantity, start: date, end: date) | |
) | |
} | |
// Add the body fat percentage to the array. | |
if let bodyFatPercentageQuantityType = self.bodyFatPercentageQuantityType { | |
let bodyFatPercentageQuantity = HKQuantity(unit: HKUnit.percent(), doubleValue: bodyFatPercentage) | |
samples.append( | |
HKQuantitySample(type: bodyFatPercentageQuantityType, quantity: bodyFatPercentageQuantity, start: date, end: date) | |
) | |
} | |
// Now that everything is packaged up, send the array to the health kit for saving. | |
healthStore.save(samples, withCompletion: successOrError) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment