public HealthKitBloodGlucoseEntry(HKQuantitySample bloodGlucoseSample) { var mgPerDL = HKUnit.FromString("mg/dL"); StartEntryDateTime = DateTimeExtensions.NSDateToDateTime(bloodGlucoseSample.StartDate); EndEntryDateTime = DateTimeExtensions.NSDateToDateTime(bloodGlucoseSample.EndDate); BloodGlucoseValue = bloodGlucoseSample.Quantity.GetDoubleValue(mgPerDL); BloodGlucoseSample = bloodGlucoseSample; }
partial void OnToggleWorkout() { if (!IsWorkoutRunning && CurrentWorkoutSession == null) { // Begin workoutt IsWorkoutRunning = true; ToggleWorkoutButton.SetTitle("Rest little Baby");; // Clear the local Active Energy Burned quantity when beginning a workout session CurrentActiveEnergyQuantity = HKQuantity.FromQuantity(HKUnit.Kilocalorie, 0.0); CurrentHeartRate = HKQuantity.FromQuantity(HKUnit.FromString("count/min"), 0.0); CurrentQuery = null; HeartRateQuery = null; ActiveEnergySamples = new List <HKSample>(); HeartRateSamples = new List <HKSample>(); // An indoor walk workout session. There are other activity and location types available to you. // Create a workout configuratio var configuration = new HKWorkoutConfiguration { ActivityType = HKWorkoutActivityType.Walking, // Why not crawling? : LocationType = HKWorkoutSessionLocationType.Indoor }; NSError error = null; CurrentWorkoutSession = new HKWorkoutSession(configuration, out error) { Delegate = this }; HealthStore.StartWorkoutSession(CurrentWorkoutSession); } else { HealthStore.EndWorkoutSession(CurrentWorkoutSession); IsWorkoutRunning = false; ResetUI(); } }
/// <summary> /// This method handles all the HealthKit gymnastics to add a blood glucose entry to the HealthKit data. /// </summary> /// <param name="entry">Entry.</param> public void AddBloodGlucoseEntry(BloodGlucoseEntry entry) { var date = new NSDate(); var quantityType = HKObjectType.GetQuantityType(HKQuantityTypeIdentifierKey.BloodGlucose); var mgPerDL = HKUnit.FromString("mg/dL"); var quantity = HKQuantity.FromQuantity(mgPerDL, entry.BloodGlucoseValue); var sample = HKQuantitySample.FromType(quantityType, quantity, date, date); HealthStore.SaveObject(sample, new Action <bool, NSError>((success, error) => { if (!success || error != null) { //There may have been an add error for some reason. AlertManager.ShowError("Health Kit", "Unable to add glucose sample: " + error); } else { //Refresh all app wide blood glucose UI fields. RefreshQuantityValue(HKQuantityTypeIdentifierKey.BloodGlucose, quantityType); } })); }
void RefreshQuantityValue(NSString quantityTypeKey, HKQuantityType quantityType) { NSSortDescriptor timeSortDescriptor = new NSSortDescriptor(HKSample.SortIdentifierEndDate, false); // Since we are interested in retrieving the user's latest sample, we sort the samples in descending order, and set the limit to 1. We are not filtering the data, and so the predicate is set to nil. HKSampleQuery query = new HKSampleQuery(quantityType, null, 100, new NSSortDescriptor[] { timeSortDescriptor }, new HKSampleQueryResultsHandler(new Action <HKSampleQuery, HKSample[], NSError>((query2, results, error) => { if (results != null && results.Length > 0) { if (quantityTypeKey == HKQuantityTypeIdentifierKey.Height) { //We have height, process the last entry into inches. var quantitySample = results.LastOrDefault() as HKQuantitySample; var quantity = quantitySample.Quantity; var heightUnit = HKUnit.Inch; DispatchQueue.MainQueue.DispatchAsync(() => { var dataStore = StateDispatcher <HealthState> .State; HealthStateMutator.MutateHeight(dataStore, () => quantity.GetDoubleValue(heightUnit)); StateDispatcher <HealthState> .Refresh(); }); } else if (quantityTypeKey == HKQuantityTypeIdentifierKey.StepCount) { DispatchQueue.MainQueue.DispatchAsync(() => { //Now we need to deliver all the blood glucose entries in a list to any listeners. var entries = new List <StepCountEntry>(); //Now also deliver all blood glucose readings up to the UI via the 'diff engine' for easy UITableViewController based updating. foreach (var entry in results) { var sample = entry as HKQuantitySample; if (sample != null) { entries.Add(new HealthKitStepCountEntry(sample)); } } HealthStateDispatchers.StepCountListStateDispatcher.Refresh( entries.Cast <StepCountEntry>().ToList()); }); } else if (quantityTypeKey == HKQuantityTypeIdentifierKey.BloodGlucose) { DispatchQueue.MainQueue.DispatchAsync(() => { //Refresh the views with the last known blood glucose quantity via HealthState and BloodGlucoseRecommendationState. var lastBloodGlucoseQuantity = (results.LastOrDefault() as HKQuantitySample).Quantity; var healthState = StateDispatcher <HealthState> .State; var mgPerDL = HKUnit.FromString("mg/dL"); HealthStateMutator.MutateBloodGlucose(healthState, () => lastBloodGlucoseQuantity.GetDoubleValue(mgPerDL)); //At this point all UI subscribers to the HealthState object will update. StateDispatcher <HealthState> .Refresh(); var recommendationStore = StateDispatcher <BloodGlucoseRecommendationState> .State; BloodGlucoseRecommendationMutator.MutateBloodGlucose( recommendationStore, () => healthState.BloodGlucose); //At this point all UI subscribers to the BloodGlucoseRecommendationState will update. StateDispatcher <BloodGlucoseRecommendationState> .Refresh(); //Now we need to deliver all the blood glucose entries in a list to any listeners. var newBloodGlucoseEntries = new List <HealthKitBloodGlucoseEntry>(); //Now also deliver all blood glucose readings up to the UI via the 'diff engine' for easy UITableViewController based updating. foreach (var bloodGlucoseEntry in results) { var bloodGlucoseSample = bloodGlucoseEntry as HKQuantitySample; if (bloodGlucoseSample != null) { newBloodGlucoseEntries.Add(new HealthKitBloodGlucoseEntry(bloodGlucoseSample)); } } HealthStateDispatchers.BloodGlucoseListStateDispatcher.Refresh( newBloodGlucoseEntries.Cast <BloodGlucoseEntry>().ToList()); }); } } }))); HealthStore.ExecuteQuery(query); }
//More info for init HKUnit: //https://developer.apple.com/documentation/healthkit/hkunit/1615733-init //More info on datatype: //https://github.com/dariosalvi78/cordova-plugin-health //TODO: come gestire active+basal per calorie? //TODO: HKQuantityTypeIdentifierActiveEnergyBurned + HKQuantityTypeIdentifierBasalEnergyBurned internal static HealthKitData ToHealthKit(this HealthDataType healthDataType) { switch (healthDataType) { case HealthDataType.StepCount: return(new HealthKitData { Unit = HKUnit.Count, TypeIdentifier = HKQuantityTypeIdentifier.StepCount, Cumulative = true }); case HealthDataType.Distance: return(new HealthKitData { Unit = HKUnit.Meter, TypeIdentifier = HKQuantityTypeIdentifier.DistanceWalkingRunning, Cumulative = true }); case HealthDataType.HeartRate: return(new HealthKitData { Unit = HKUnit.Count.UnitDividedBy(HKUnit.Minute), TypeIdentifier = HKQuantityTypeIdentifier.HeartRate }); case HealthDataType.Height: return(new HealthKitData { Unit = HKUnit.Meter, TypeIdentifier = HKQuantityTypeIdentifier.Height }); case HealthDataType.Weight: return(new HealthKitData { Unit = HKUnit.Gram, TypeIdentifier = HKQuantityTypeIdentifier.BodyMass }); case HealthDataType.Calories: return(new HealthKitData { Unit = HKUnit.Kilocalorie, TypeIdentifier = HKQuantityTypeIdentifier.ActiveEnergyBurned, Cumulative = true }); case HealthDataType.CaloriesActive: return(new HealthKitData { Unit = HKUnit.Kilocalorie, TypeIdentifier = HKQuantityTypeIdentifier.ActiveEnergyBurned, Cumulative = true }); /*case HealthDataType.CaloriesBasal: * return new HealthKitData * { * Unit = HKUnit.Kilocalorie, * TypeIdentifier = HKQuantityTypeIdentifier.BasalEnergyBurned, * Cumulative = true * };*/ case HealthDataType.Water: return(new HealthKitData { Unit = HKUnit.Liter, TypeIdentifier = HKQuantityTypeIdentifier.DietaryWater, Cumulative = true }); case HealthDataType.BodyFat: return(new HealthKitData { Unit = HKUnit.Percent, TypeIdentifier = HKQuantityTypeIdentifier.BodyFatPercentage }); case HealthDataType.BodyTemperature: return(new HealthKitData { Unit = HKUnit.DegreeCelsius, TypeIdentifier = HKQuantityTypeIdentifier.BodyTemperature }); case HealthDataType.BloodPressureSystolic: return(new HealthKitData { Unit = HKUnit.MillimeterOfMercury, TypeIdentifier = HKQuantityTypeIdentifier.BloodPressureSystolic }); case HealthDataType.BloodPressureDiastolic: return(new HealthKitData { Unit = HKUnit.MillimeterOfMercury, TypeIdentifier = HKQuantityTypeIdentifier.BloodPressureDiastolic }); case HealthDataType.BloodOxygen: return(new HealthKitData { Unit = HKUnit.Percent, TypeIdentifier = HKQuantityTypeIdentifier.OxygenSaturation }); case HealthDataType.BloodGlucose: return(new HealthKitData { Unit = HKUnit.FromString("mg/dl"), TypeIdentifier = HKQuantityTypeIdentifier.BloodGlucose }); // IOS SPECIFIC case HealthDataType.iOS_BodyMassIndex: return(new HealthKitData { Unit = HKUnit.FromString(""), TypeIdentifier = HKQuantityTypeIdentifier.BodyMassIndex }); case HealthDataType.iOS_WalkingHeartRate: return(new HealthKitData { Unit = HKUnit.Count.UnitDividedBy(HKUnit.Minute), TypeIdentifier = HKQuantityTypeIdentifier.WalkingHeartRateAverage }); case HealthDataType.iOS_RestingHeartRate: return(new HealthKitData { Unit = HKUnit.Count.UnitDividedBy(HKUnit.Minute), TypeIdentifier = HKQuantityTypeIdentifier.RestingHeartRate }); case HealthDataType.iOS_BasalEnergyBurned: return(new HealthKitData { Unit = HKUnit.Kilocalorie, TypeIdentifier = HKQuantityTypeIdentifier.BasalEnergyBurned, Cumulative = true }); case HealthDataType.iOS_WaistCircumference: return(new HealthKitData { Unit = HKUnit.Meter, TypeIdentifier = HKQuantityTypeIdentifier.WaistCircumference }); case HealthDataType.iOS_StandTime: ThrowIfUnsupported(13, 0); return(new HealthKitData { Unit = HKUnit.Minute, TypeIdentifier = HKQuantityTypeIdentifier.AppleStandTime, Cumulative = true }); case HealthDataType.iOS_ExerciseTime: return(new HealthKitData { Unit = HKUnit.Minute, TypeIdentifier = HKQuantityTypeIdentifier.AppleExerciseTime, Cumulative = true }); default: throw new ArgumentOutOfRangeException(); } }
public void BeginWorkout(DateTime beginDate) { // Obtain the `HKObjectType` for active energy burned and the `HKUnit` for kilocalories. var activeEnergyType = HKQuantityType.Create(HKQuantityTypeIdentifier.ActiveEnergyBurned); if (activeEnergyType == null) { return; } // Obtain the `HKObjectType` for HeartRate. var heartRateType = HKQuantityType.Create(HKQuantityTypeIdentifier.HeartRate); if (heartRateType == null) { return; } var energyUnit = HKUnit.Kilocalorie; var heartRateUnit = HKUnit.FromString("count/min"); // Update properties. WorkoutBeginDate = beginDate; // Set up a predicate to obtain only samples from the local device starting from `beginDate`. var datePredicate = HKQuery.GetPredicateForSamples((NSDate)beginDate, null, HKQueryOptions.None); var devices = new NSSet <HKDevice>(new HKDevice[] { HKDevice.LocalDevice }); var devicePredicate = HKQuery.GetPredicateForObjectsFromDevices(devices); var predicate = NSCompoundPredicate.CreateAndPredicate(new NSPredicate[] { datePredicate, devicePredicate }); //Create a results handler to recreate the samples generated by a query of active energy samples so that they can be associated with this app in the move graph.It should be noted that if your app has different heuristics for active energy burned you can generate your own quantities rather than rely on those from the watch.The sum of your sample's quantity values should equal the energy burned value provided for the workout Action <List <HKSample> > sampleHandler; sampleHandler = (List <HKSample> samples) => { DispatchQueue.MainQueue.DispatchAsync(delegate { var accumulatedSamples = new List <HKQuantitySample>(); var initialActivityEnergy = CurrentActiveEnergyQuantity.GetDoubleValue(energyUnit); double accumulatedValue = initialActivityEnergy; foreach (HKQuantitySample sample in samples) { accumulatedValue = accumulatedValue + sample.Quantity.GetDoubleValue(energyUnit); var ourSample = HKQuantitySample.FromType(activeEnergyType, sample.Quantity, sample.StartDate, sample.EndDate); accumulatedSamples.Add(ourSample); } // Update the UI. CurrentActiveEnergyQuantity = HKQuantity.FromQuantity(energyUnit, accumulatedValue); EnergyLabel.SetText($"Energy: {accumulatedValue.ToString("F")}"); // Update our samples. ActiveEnergySamples.AddRange(accumulatedSamples); }); }; // Create a query to report new Active Energy Burned samples to our app. var activeEnergyQuery = new HKAnchoredObjectQuery(activeEnergyType, predicate, null, HKSampleQuery.NoLimit, (query, addedObjects, deletedObjects, newAnchor, error) => { if (error == null) { // NOTE: `deletedObjects` are not considered in the handler as there is no way to delete samples from the watch during a workout ActiveEnergySamples = new List <HKSample>(addedObjects); sampleHandler(ActiveEnergySamples); } else { Console.WriteLine($"An error occured executing the query. In your app, try to handle this gracefully. The error was: {error}."); } }); // Assign the same handler to process future samples generated while the query is still active. activeEnergyQuery.UpdateHandler = (query, addedObjects, deletedObjects, newAnchor, error) => { if (error == null) { ActiveEnergySamples = new List <HKSample>(addedObjects); sampleHandler(ActiveEnergySamples); } else { Console.WriteLine($"An error occured executing the query. In your app, try to handle this gracefully. The error was: {error}."); } }; // Start Query CurrentQuery = activeEnergyQuery; HealthStore.ExecuteQuery(activeEnergyQuery); Action <List <HKSample> > heartRateSampler = (List <HKSample> samples) => { foreach (HKQuantitySample sample in samples) { _dangerousHeartRate.OnNext(sample.Quantity.GetDoubleValue(heartRateUnit)); } DispatchQueue.MainQueue.DispatchAsync(delegate { var sample = samples.LastOrDefault() as HKQuantitySample; if (sample != null) { HeartRateLabel.SetText($"Heart Rate: {sample.Quantity.GetDoubleValue(heartRateUnit).ToString()}"); } }); }; HeartRateQuery = new HKAnchoredObjectQuery(heartRateType, predicate, null, HKSampleQuery.NoLimit, (query, addedObjects, deletedObjects, newAnchor, error) => { if (error == null) { // NOTE: `deletedObjects` are not considered in the handler as there is no way to delete samples from the watch during a workout sampleHandler(new List <HKSample>(addedObjects)); } else { Console.WriteLine($"An error occured executing the query. In your app, try to handle this gracefully. The error was: {error}."); } }); // Assign the same handler to process future samples generated while the query is still active. (HeartRateQuery as HKAnchoredObjectQuery).UpdateHandler = (query, addedObjects, deletedObjects, newAnchor, error) => { if (error == null) { heartRateSampler(new List <HKSample>(addedObjects)); } else { Console.WriteLine($"An error occured executing the query. In your app, try to handle this gracefully. The error was: {error}."); } }; HealthStore.ExecuteQuery(HeartRateQuery); }