/* Copyright (C) 2015 Apple Inc. All Rights Reserved. See LICENSE.txt for this sample’s licensing information Abstract: This file shows an example of implementing the OperationCondition protocol. */ #if os(iOS) import HealthKit import UIKit /** A condition to indicate an `EarthquakeOperation` requires access to the user's health data. */ struct HealthCondition: OperationCondition { static let name = "Health" static let healthDataAvailable = "HealthDataAvailable" static let unauthorizedShareTypesKey = "UnauthorizedShareTypes" static let isMutuallyExclusive = false let shareTypes: Set let readTypes: Set /** The designated initializer. - parameter typesToWrite: An array of `HKSampleType` objects, indicating the kinds of data you wish to save to HealthKit. - parameter typesToRead: An array of `HKSampleType` objects, indicating the kinds of data you wish to read from HealthKit. */ init(typesToWrite: Set, typesToRead: Set) { shareTypes = typesToWrite readTypes = typesToRead } func dependencyForOperation(operation: EarthquakeOperation) -> Operation? { guard HKHealthStore.isHealthDataAvailable() else { return nil } guard !shareTypes.isEmpty || !readTypes.isEmpty else { return nil } return HealthPermissionOperation(shareTypes: shareTypes, readTypes: readTypes) } func evaluateForOperation(operation: EarthquakeOperation, completion: (OperationConditionResult) -> Void) { guard HKHealthStore.isHealthDataAvailable() else { failed(unauthorizedShareTypes: shareTypes, completion: completion) return } let store = HKHealthStore() /* Note that we cannot check to see if access to the "typesToRead" has been granted or not, as that is sensitive data. For example, a person with diabetes may choose to not allow access to Blood Glucose data, and the fact that this request has denied is itself an indicator that the user may have diabetes. Thus, we can only check to see if we've been given permission to write data to HealthKit. */ let unauthorizedShareTypes = shareTypes.filter { shareType in return store.authorizationStatus(for: shareType) != .sharingAuthorized } if !unauthorizedShareTypes.isEmpty { failed(unauthorizedShareTypes: Set(unauthorizedShareTypes), completion: completion) } else { completion(.Satisfied) } } // Break this out in to its own method so we don't clutter up the evaluate... method. private func failed(unauthorizedShareTypes: Set, completion: (OperationConditionResult) -> Void) { let error = NSError(code: .ConditionFailed, userInfo: [ OperationConditionKey: type(of: self).name, type(of: self).healthDataAvailable: HKHealthStore.isHealthDataAvailable(), type(of: self).unauthorizedShareTypesKey: unauthorizedShareTypes ]) completion(.Failed(error)) } } /** A private `EarthquakeOperation` that will request access to the user's health data, if it has not already been granted. */ private class HealthPermissionOperation: EarthquakeOperation { let shareTypes: Set let readTypes: Set init(shareTypes: Set, readTypes: Set) { self.shareTypes = shareTypes self.readTypes = readTypes super.init() addCondition(condition: MutuallyExclusive()) addCondition(condition: MutuallyExclusive()) addCondition(condition: AlertPresentation()) } override func execute() { DispatchQueue.main.async { let store = HKHealthStore() /* This method is smart enough to not re-prompt for access if access has already been granted. */ store.requestAuthorization(toShare: self.shareTypes, read: self.readTypes) { completed, error in self.finish() } } } } #endif