#import "RCTAppleHealthKit+Methods_Vitals.h" #import "RCTAppleHealthKit+Queries.h" #import "RCTAppleHealthKit+Utils.h" @implementation RCTAppleHealthKit (Methods_Vitals) - (void)vitals_getHeartRateSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback { HKQuantityType *heartRateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]; HKUnit *count = [HKUnit countUnit]; HKUnit *minute = [HKUnit minuteUnit]; HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[count unitDividedByUnit:minute]]; NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false]; NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; if(startDate == nil){ callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]); return; } NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate]; [self fetchQuantitySamplesOfType:heartRateType unit:unit predicate:predicate ascending:ascending limit:limit completion:^(NSArray *results, NSError *error) { if(results){ callback(@[[NSNull null], results]); return; } else { NSLog(@"error getting heart rate samples: %@", error); callback(@[RCTMakeError(@"error getting heart rate samples", nil, nil)]); return; } }]; } - (void)vitals_getHeartRateSamplesByAnchor:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback { HKQuantityType *heartRateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]; HKUnit *count = [HKUnit countUnit]; HKUnit *minute = [HKUnit minuteUnit]; HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[count unitDividedByUnit:minute]]; NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false]; NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; if(startDate == nil){ callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]); return; } NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate]; [self fetchQuantitySamplesOfTypeByAnchor:heartRateType unit:unit predicate:predicate ascending:ascending limit:limit completion:^(NSDictionary *results, NSError *error) { if(results){ callback(@[[NSNull null], results]); return; } else { NSLog(@"error getting heart rate samples: %@", error); callback(@[RCTMakeError(@"error getting heart rate samples", nil, nil)]); return; } }]; } - (void)vitals_getBodyTemperatureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback { HKQuantityType *bodyTemperatureType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature]; HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit degreeCelsiusUnit]]; NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false]; NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; if(startDate == nil){ callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]); return; } NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate]; [self fetchQuantitySamplesOfType:bodyTemperatureType unit:unit predicate:predicate ascending:ascending limit:limit completion:^(NSArray *results, NSError *error) { if(results){ callback(@[[NSNull null], results]); return; } else { NSLog(@"error getting body temperature samples: %@", error); callback(@[RCTMakeError(@"error getting body temperature samples", nil, nil)]); return; } }]; } - (void)vitals_getBloodPressureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback { HKCorrelationType *bloodPressureCorrelationType = [HKCorrelationType correlationTypeForIdentifier:HKCorrelationTypeIdentifierBloodPressure]; HKQuantityType *systolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic]; HKQuantityType *diastolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic]; HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit millimeterOfMercuryUnit]]; NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false]; NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; if(startDate == nil){ callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]); return; } NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate]; [self fetchCorrelationSamplesOfType:bloodPressureCorrelationType unit:unit predicate:predicate ascending:ascending limit:limit completion:^(NSArray *results, NSError *error) { if(results){ NSMutableArray *data = [NSMutableArray arrayWithCapacity:1]; for (NSDictionary *sample in results) { HKCorrelation *bloodPressureValues = [sample valueForKey:@"correlation"]; HKQuantitySample *bloodPressureSystolicValue = [bloodPressureValues objectsForType:systolicType].anyObject; HKQuantitySample *bloodPressureDiastolicValue = [bloodPressureValues objectsForType:diastolicType].anyObject; NSString *deviceName = bloodPressureSystolicValue.device.name ? bloodPressureSystolicValue.device.name : @""; NSString *deviceManufacturer = bloodPressureSystolicValue.device.manufacturer ? bloodPressureSystolicValue.device.manufacturer : @""; NSString *deviceHardwareVer = bloodPressureSystolicValue.device.hardwareVersion ? bloodPressureSystolicValue.device.hardwareVersion : @""; NSString *deviceSoftwareVer = bloodPressureSystolicValue.device.softwareVersion ? bloodPressureSystolicValue.device.softwareVersion : @""; NSString *sourceName = bloodPressureSystolicValue.sourceRevision.source.name; NSString *sourceId = bloodPressureSystolicValue.sourceRevision.source.bundleIdentifier; NSString *uuid = bloodPressureSystolicValue.UUID.UUIDString; NSError *error; NSDictionary *metaDict; if(bloodPressureSystolicValue.metadata == nil){ metaDict = [[NSDictionary alloc] init]; }else{ metaDict = bloodPressureSystolicValue.metadata; } NSData *jsonData = [NSJSONSerialization dataWithJSONObject:metaDict options:NSJSONWritingPrettyPrinted error:&error]; NSString *metadata =[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; NSDictionary *elem = @{ @"bloodPressureSystolicValue" : @([bloodPressureSystolicValue.quantity doubleValueForUnit:unit]), @"bloodPressureDiastolicValue" : @([bloodPressureDiastolicValue.quantity doubleValueForUnit:unit]), @"startDate" : [sample valueForKey:@"startDate"], @"endDate" : [sample valueForKey:@"endDate"], @"deviceName" : deviceName, @"deviceManufacturer" : deviceManufacturer, @"deviceHardware" : deviceHardwareVer, @"deviceSoftware" : deviceSoftwareVer, @"sourceName" : sourceName, @"sourceId" : sourceId, @"unit" : unit.description, @"uuid" : uuid, @"metadata" : metadata }; [data addObject:elem]; } callback(@[[NSNull null], data]); return; } else { NSLog(@"error getting blood pressure samples: %@", error); callback(@[RCTMakeError(@"error getting blood pressure samples", nil, nil)]); return; } }]; } - (void)vitals_getBloodPressureSamplesByAnchor:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback { HKCorrelationType *bloodPressureCorrelationType = [HKCorrelationType correlationTypeForIdentifier:HKCorrelationTypeIdentifierBloodPressure]; HKQuantityType *systolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic]; HKQuantityType *diastolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic]; HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit millimeterOfMercuryUnit]]; NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false]; NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; if(startDate == nil){ callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]); return; } NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate]; [self fetchCorrelationSamplesOfTypeByAnchor:bloodPressureCorrelationType unit:unit predicate:predicate ascending:ascending limit:limit completion:^(NSDictionary *results, NSError *error) { if(results){ NSMutableArray *data = [NSMutableArray arrayWithCapacity:1]; for (NSDictionary *sample in [results objectForKey:@"samples"]) { HKCorrelation *bloodPressureValues = [sample valueForKey:@"correlation"]; HKQuantitySample *bloodPressureSystolicValue = [bloodPressureValues objectsForType:systolicType].anyObject; HKQuantitySample *bloodPressureDiastolicValue = [bloodPressureValues objectsForType:diastolicType].anyObject; NSString *deviceName = bloodPressureSystolicValue.device.name ? bloodPressureSystolicValue.device.name : @""; NSString *deviceManufacturer = bloodPressureSystolicValue.device.manufacturer ? bloodPressureSystolicValue.device.manufacturer : @""; NSString *deviceHardwareVer = bloodPressureSystolicValue.device.hardwareVersion ? bloodPressureSystolicValue.device.hardwareVersion : @""; NSString *deviceSoftwareVer = bloodPressureSystolicValue.device.softwareVersion ? bloodPressureSystolicValue.device.softwareVersion : @""; NSString *sourceName = bloodPressureSystolicValue.sourceRevision.source.name; NSString *sourceId = bloodPressureSystolicValue.sourceRevision.source.bundleIdentifier; NSString *uuid = bloodPressureValues.UUID.UUIDString; NSError *error; NSDictionary *metaDict; if(bloodPressureSystolicValue.metadata == nil){ metaDict = [[NSDictionary alloc] init]; }else{ metaDict = bloodPressureSystolicValue.metadata; } NSData *jsonData = [NSJSONSerialization dataWithJSONObject:metaDict options:NSJSONWritingPrettyPrinted error:&error]; NSString *metadata =[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; NSDictionary *elem = @{ @"bloodPressureSystolicValue" : @([bloodPressureSystolicValue.quantity doubleValueForUnit:unit]), @"bloodPressureDiastolicValue" : @([bloodPressureDiastolicValue.quantity doubleValueForUnit:unit]), @"startDate" : [sample valueForKey:@"startDate"], @"endDate" : [sample valueForKey:@"endDate"], @"deviceName" : deviceName, @"deviceManufacturer" : deviceManufacturer, @"deviceHardware" : deviceHardwareVer, @"deviceSoftware" : deviceSoftwareVer, @"sourceName" : sourceName, @"sourceId" : sourceId, @"unit" : unit.description, @"uuid" : uuid, @"metadata" : metadata }; [data addObject:elem]; } NSDictionary *result = @{ @"samples" : data, @"deleteSamples" : [results objectForKey:@"deleteSamples"] }; callback(@[[NSNull null], result]); return; } else { NSLog(@"error getting blood pressure samples by anchor: %@", error); callback(@[RCTMakeError(@"error getting blood pressure samples by anchor", nil, nil)]); return; } }]; } - (void)vitals_getRespiratoryRateSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback { HKQuantityType *respiratoryRateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierRespiratoryRate]; HKUnit *count = [HKUnit countUnit]; HKUnit *minute = [HKUnit minuteUnit]; HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[count unitDividedByUnit:minute]]; NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false]; NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; if(startDate == nil){ callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]); return; } NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate]; [self fetchQuantitySamplesOfType:respiratoryRateType unit:unit predicate:predicate ascending:ascending limit:limit completion:^(NSArray *results, NSError *error) { if(results){ callback(@[[NSNull null], results]); return; } else { NSLog(@"error getting respiratory rate samples: %@", error); callback(@[RCTMakeError(@"error getting respiratory rate samples", nil, nil)]); return; } }]; } @end