diff --git a/RCTAppleHealthKit.xcodeproj/project.pbxproj b/RCTAppleHealthKit.xcodeproj/project.pbxproj index b0dedf21a65c18cfa2fe7e082ec19d30aefe0cf6..81f6100934e8485a1db9e608d45475ddfb772c25 100644 --- a/RCTAppleHealthKit.xcodeproj/project.pbxproj +++ b/RCTAppleHealthKit.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 3774C8D41D20C6390000B3F3 /* RCTAppleHealthKit+Methods_Body.m in Sources */ = {isa = PBXBuildFile; fileRef = 3774C8D31D20C6390000B3F3 /* RCTAppleHealthKit+Methods_Body.m */; }; 3774C8D71D20C65F0000B3F3 /* RCTAppleHealthKit+Methods_Fitness.m in Sources */ = {isa = PBXBuildFile; fileRef = 3774C8D61D20C65F0000B3F3 /* RCTAppleHealthKit+Methods_Fitness.m */; }; 377D44F31D247D0A004E35CB /* RCTAppleHealthKit+Methods_Characteristic.m in Sources */ = {isa = PBXBuildFile; fileRef = 377D44F21D247D0A004E35CB /* RCTAppleHealthKit+Methods_Characteristic.m */; }; + 64C42D4A1D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.m in Sources */ = {isa = PBXBuildFile; fileRef = 64C42D491D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.m */; }; + 64E0E73F1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.m in Sources */ = {isa = PBXBuildFile; fileRef = 64E0E73E1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -46,6 +48,10 @@ 3774C8D61D20C65F0000B3F3 /* RCTAppleHealthKit+Methods_Fitness.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTAppleHealthKit+Methods_Fitness.m"; sourceTree = ""; }; 377D44F11D247D0A004E35CB /* RCTAppleHealthKit+Methods_Characteristic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTAppleHealthKit+Methods_Characteristic.h"; sourceTree = ""; }; 377D44F21D247D0A004E35CB /* RCTAppleHealthKit+Methods_Characteristic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTAppleHealthKit+Methods_Characteristic.m"; sourceTree = ""; }; + 64C42D481D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTAppleHealthKit+Methods_Vitals.h"; sourceTree = ""; }; + 64C42D491D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTAppleHealthKit+Methods_Vitals.m"; sourceTree = ""; }; + 64E0E73D1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTAppleHealthKit+Methods_Results.h"; sourceTree = ""; }; + 64E0E73E1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTAppleHealthKit+Methods_Results.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -78,6 +84,10 @@ 3774C88F1D2092F20000B3F3 /* RCTAppleHealthKit */ = { isa = PBXGroup; children = ( + 64E0E73D1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.h */, + 64E0E73E1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.m */, + 64C42D481D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.h */, + 64C42D491D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.m */, 3774C8901D2092F20000B3F3 /* RCTAppleHealthKit.h */, 3774C8921D2092F20000B3F3 /* RCTAppleHealthKit.m */, 3774C8991D2095450000B3F3 /* RCTAppleHealthKit+Queries.h */, @@ -155,6 +165,8 @@ 3774C89B1D2095450000B3F3 /* RCTAppleHealthKit+Queries.m in Sources */, 3774C8A11D20A6B90000B3F3 /* RCTAppleHealthKit+Utils.m in Sources */, 3774C8D41D20C6390000B3F3 /* RCTAppleHealthKit+Methods_Body.m in Sources */, + 64E0E73F1D37947B00EAB905 /* RCTAppleHealthKit+Methods_Results.m in Sources */, + 64C42D4A1D351A8800A0A8F7 /* RCTAppleHealthKit+Methods_Vitals.m in Sources */, 3774C8931D2092F20000B3F3 /* RCTAppleHealthKit.m in Sources */, 377D44F31D247D0A004E35CB /* RCTAppleHealthKit+Methods_Characteristic.m in Sources */, 3774C89E1D2095850000B3F3 /* RCTAppleHealthKit+TypesAndPermissions.m in Sources */, diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.h b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.h new file mode 100644 index 0000000000000000000000000000000000000000..cd43e7e49d3aef357c937d504f9967011d647829 --- /dev/null +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.h @@ -0,0 +1,7 @@ +#import "RCTAppleHealthKit.h" + +@interface RCTAppleHealthKit (Methods_Results) + +- (void)results_getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; + +@end diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.m b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.m new file mode 100644 index 0000000000000000000000000000000000000000..19c7c7c221c7f012f53acb7bf454c6784bf8ac28 --- /dev/null +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.m @@ -0,0 +1,42 @@ +#import "RCTAppleHealthKit+Methods_Results.h" +#import "RCTAppleHealthKit+Queries.h" +#import "RCTAppleHealthKit+Utils.h" + +@implementation RCTAppleHealthKit (Methods_Results) + +- (void)results_getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback +{ + HKQuantityType *bloodGlucoseType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose]; + + HKUnit *mmoLPerL = [[HKUnit moleUnitWithMetricPrefix:HKMetricPrefixMilli molarMass:HKUnitMolarMassBloodGlucose] unitDividedByUnit:[HKUnit literUnit]]; + + HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:mmoLPerL]; + 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:bloodGlucoseType + unit:unit + predicate:predicate + ascending:ascending + limit:limit + completion:^(NSArray *results, NSError *error) { + if(results){ + callback(@[[NSNull null], results]); + return; + } else { + NSLog(@"error getting blood glucose samples: %@", error); + callback(@[RCTMakeError(@"error getting blood glucose samples", nil, nil)]); + return; + } + }]; +} + + +@end diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Vitals.h b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Vitals.h new file mode 100644 index 0000000000000000000000000000000000000000..885f7bed94a394909fac63c43a145cfa9b156a07 --- /dev/null +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Vitals.h @@ -0,0 +1,13 @@ +#import "RCTAppleHealthKit.h" + +@interface RCTAppleHealthKit (Methods_Vitals) + +- (void)vitals_getHeartRateSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; + +- (void)vitals_getBodyTemperatureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; + +- (void)vitals_getBloodPressureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; + +- (void)vitals_getRespiratoryRateSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; + +@end diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Vitals.m b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Vitals.m new file mode 100644 index 0000000000000000000000000000000000000000..df52d836247309d0675e320d2c745376d99c97b4 --- /dev/null +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Vitals.m @@ -0,0 +1,166 @@ +#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_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; + + NSDictionary *elem = @{ + @"bloodPressureSystolicValue" : @([bloodPressureSystolicValue.quantity doubleValueForUnit:unit]), + @"bloodPressureDiastolicValue" : @([bloodPressureDiastolicValue.quantity doubleValueForUnit:unit]), + @"startDate" : [sample valueForKey:@"startDate"], + @"endDate" : [sample valueForKey:@"endDate"], + }; + + [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_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 diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h index 7b9134966251baed7be8fca24d8ee4f32ebcc59d..69449c28c6b9fdde7afb27e6732363f097840c7b 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h @@ -21,7 +21,6 @@ completion:(void (^)(NSArray *, NSError *))completionHandler; - - (void)fetchQuantitySamplesOfType:(HKQuantityType *)quantityType unit:(HKUnit *)unit predicate:(NSPredicate *)predicate @@ -30,6 +29,12 @@ completion:(void (^)(NSArray *, NSError *))completion; +- (void)fetchCorrelationSamplesOfType:(HKQuantityType *)quantityType + unit:(HKUnit *)unit + predicate:(NSPredicate *)predicate + ascending:(BOOL)asc + limit:(NSUInteger)lim + completion:(void (^)(NSArray *, NSError *))completion; - (void)fetchCumulativeSumStatisticsCollection:(HKQuantityType *)quantityType diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m index 491fe62b96681a5449716ff97fc10bc24378068c..41e6398d22931c69ad83f6d14b490c9454b781d2 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m @@ -108,7 +108,60 @@ } +- (void)fetchCorrelationSamplesOfType:(HKQuantityType *)quantityType + unit:(HKUnit *)unit + predicate:(NSPredicate *)predicate + ascending:(BOOL)asc + limit:(NSUInteger)lim + completion:(void (^)(NSArray *, NSError *))completion { + + NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate + ascending:asc]; + + // declare the block + void (^handlerBlock)(HKSampleQuery *query, NSArray *results, NSError *error); + // create and assign the block + handlerBlock = ^(HKSampleQuery *query, NSArray *results, NSError *error) { + if (!results) { + if (completion) { + completion(nil, error); + } + return; + } + + if (completion) { + NSMutableArray *data = [NSMutableArray arrayWithCapacity:1]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + for (HKCorrelation *sample in results) { + + NSString *startDateString = [RCTAppleHealthKit buildISO8601StringFromDate:sample.startDate]; + NSString *endDateString = [RCTAppleHealthKit buildISO8601StringFromDate:sample.endDate]; + + NSDictionary *elem = @{ + @"correlation" : sample, + @"startDate" : startDateString, + @"endDate" : endDateString, + }; + + [data addObject:elem]; + } + + completion(data, error); + }); + } + }; + + HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:quantityType + predicate:predicate + limit:lim + sortDescriptors:@[timeSortDescriptor] + resultsHandler:handlerBlock]; + + [self.healthStore executeQuery:query]; +} diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m b/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m index 4c5a3aaa3e7e040f66dd6c4d6c59fefb4a4941bc..ac91a98853b743c6fae8694a40fdcbe1989fc447 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m @@ -36,6 +36,14 @@ @"AppleExerciseTime" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierAppleExerciseTime], // Nutrition Identifiers @"DietaryEnergy" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryEnergyConsumed], + // Vital Signs Identifiers + @"HeartRate" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate], + @"BodyTemperature" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature], + @"BloodPressureSystolic" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic], + @"BloodPressureDiastolic" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic], + @"RespiratoryRate" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierRespiratoryRate], + // Results Identifiers + @"BloodGlucose" : [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose], }; return readPerms; } diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m b/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m index d2e6aad9f3ce1d179c5575f0f00350efc8bfde23..51e3f636ad309bfe4f355625a09f682d4ec6cb43 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m @@ -225,6 +225,27 @@ if([unitString isEqualToString:@"percent"]){ theUnit = [HKUnit percentUnit]; } + if([unitString isEqualToString:@"bpm"]){ + HKUnit *count = [HKUnit countUnit]; + HKUnit *minute = [HKUnit minuteUnit]; + + theUnit = [count unitDividedByUnit:minute]; + } + if([unitString isEqualToString:@"fahrenheit"]){ + theUnit = [HKUnit degreeFahrenheitUnit]; + } + if([unitString isEqualToString:@"celsius"]){ + theUnit = [HKUnit degreeCelsiusUnit]; + } + if([unitString isEqualToString:@"mmhg"]){ + theUnit = [HKUnit millimeterOfMercuryUnit]; + } + if([unitString isEqualToString:@"mmolPerL"]){ + theUnit = [[HKUnit moleUnitWithMetricPrefix:HKMetricPrefixMilli molarMass:HKUnitMolarMassBloodGlucose] unitDividedByUnit:[HKUnit literUnit]]; + } + if([unitString isEqualToString:@"mgPerdL"]){ + theUnit = [HKUnit unitFromString:@"mg/dL"]; + } if(theUnit == nil){ theUnit = defaultValue; diff --git a/RCTAppleHealthKit/RCTAppleHealthKit.m b/RCTAppleHealthKit/RCTAppleHealthKit.m index 2105fdef4fd799e104123f27ee5eddbcc159c647..f016e75b7f0045f07e3f40603024b1e8b498131c 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit.m @@ -14,6 +14,8 @@ #import "RCTAppleHealthKit+Methods_Body.h" #import "RCTAppleHealthKit+Methods_Fitness.h" #import "RCTAppleHealthKit+Methods_Characteristic.h" +#import "RCTAppleHealthKit+Methods_Vitals.h" +#import "RCTAppleHealthKit+Methods_Results.h" @implementation RCTAppleHealthKit @@ -134,6 +136,30 @@ RCT_EXPORT_METHOD(getFlightsClimbed:(NSDictionary *)input callback:(RCTResponseS [self fitness_getFlightsClimbedOnDay:input callback:callback]; } +RCT_EXPORT_METHOD(getHeartRateSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self vitals_getHeartRateSamples:input callback:callback]; +} + +RCT_EXPORT_METHOD(getBodyTemperatureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self vitals_getBodyTemperatureSamples:input callback:callback]; +} + +RCT_EXPORT_METHOD(getBloodPressureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self vitals_getBloodPressureSamples:input callback:callback]; +} + +RCT_EXPORT_METHOD(getRespiratoryRateSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self vitals_getRespiratoryRateSamples:input callback:callback]; +} + +RCT_EXPORT_METHOD(getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self results_getBloodGlucoseSamples:input callback:callback]; +} RCT_EXPORT_METHOD(getInfo:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) diff --git a/README.md b/README.md index d869bb7dbdbe790eef66f6926a266072e72b2aa1..506ab9ec50d7cb50857bbff1a615fa4f58be7689 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,11 @@ A React Native bridge module for interacting with [Apple HealthKit] data. * [saveBmi](#savebmi) * [getLatestBodyFatPercentage](#getlatestbodyfatpercentage) * [getLatestLeanBodyMass](#getlatestleanbodymass) + * [getHeartRateSamples](#getheartratesamples) + * [getBodyTemperatureSamples](#getbodytemperaturesamples) + * [getBloodPressureSamples](#getbloodpressuresamples) + * [getRespiratoryRateSamples](#getrespiratoryratesamples) + * [getBloodGlucoseSamples](#getbloodglucosesamples) * [Examples](#examples) @@ -143,6 +148,13 @@ The available HealthKit permissions to use with `initHealthKit` | NikeFuel | [HKQuantityTypeIdentifierNikeFuel](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifiernikefuel?language=objc) | ✓ | | | AppleExerciseTime | [HKQuantityTypeIdentifierAppleExerciseTime](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierappleexercisetime?language=objc) | ✓ | | | DietaryEnergy | [HKQuantityTypeIdentifierDietaryEnergyConsumed](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierdietaryenergyconsumed?language=objc) | ✓ | ✓ | +| HeartRate | [HKQuantityTypeIdentifierHeartRate](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierheartrate?language=objc) | ✓ | | +| BodyTemperature | [HKQuantityTypeIdentifierBodyTemperature](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierbodytemperature?language=objc) | ✓ | | +| BloodPressureSystolic | [HKQuantityTypeIdentifierBloodPressureSystolic](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierbloodpressuresystolic?language=objc) | ✓ | | +| BloodPressureDiastolic | [HKQuantityTypeIdentifierBloodPressureDiastolic](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierbloodpressurediastolic?language=objc) | ✓ | | +| RespiratoryRate | [HKQuantityTypeIdentifierRespiratoryRate](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierrespiratoryrate?language=objc) | ✓ | | +| BloodGlucose | [HKQuantityTypeIdentifierBloodGlucose](https://developer.apple.com/reference/healthkit/hkquantitytypeidentifierbloodglucose?language=objc) | ✓ | | + These permissions are exported as constants of the `react-native-apple-healthkit` module. @@ -715,6 +727,148 @@ On success, the callback function will be provided with a `leanBodyMass` object }); ``` +___ + +#### **`getHeartRateSamples`** +query for heart rate samples. the options object is used to setup a query to retrieve relevant samples. +```javascript +let options = { + unit: 'bpm', // optional; default 'bpm' + startDate: (new Date(2016,4,27)).toISOString(), // required + endDate: (new Date()).toISOString(), // optional; default now + ascending: false, // optional; default false + limit:10, // optional; default no limit +}; +``` + +the callback function will be called with a `samples` array containing objects with *value*, *startDate*, and *endDate* fields + +*example usage* +```javascript +AppleHealthKit.getHeartRateSamples(options, (err: Object, samples: Array) => { + if(this._handleHealthKitError(err, 'getHeartRateSamples')){ + return; + } + // use samples ... +}); +``` + +___ + +#### **`getBodyTemperatureSamples`** +query for body temperature samples. the options object is used to setup a query to retrieve relevant samples. +```javascript +let options = { + unit: 'celsius', // optional; default 'celsius' + startDate: (new Date(2016,4,27)).toISOString(), // required + endDate: (new Date()).toISOString(), // optional; default now + ascending: false, // optional; default false + limit:10, // optional; default no limit +}; +``` +available units are: `'fahrenheit'`, `'celsius'`. + +the callback function will be called with a `samples` array containing objects with *value*, *startDate*, and *endDate* fields. + +*example usage* +```javascript +AppleHealthKit.getBodyTemperatureSamples(options, (err: Object, samples: Array) => { + if(this._handleHealthKitError(err, 'getBodyTemperatureSamples')){ + return; + } + // use samples ... +}); +``` + +___ + +#### **`getBloodPressureSamples`** +query for blood pressure samples. the options object is used to setup a query to retrieve relevant samples. +```javascript +let options = { + unit: 'mmhg', // optional; default 'mmhg' + startDate: (new Date(2016,4,27)).toISOString(), // required + endDate: (new Date()).toISOString(), // optional; default now + ascending: false, // optional; default false + limit:10, // optional; default no limit +}; +``` + +the callback function will be called with a `samples` array containing objects with *bloodPressureSystolicValue*, *bloodPressureDiastolicValue*, *startDate*, and *endDate* fields +```javascript +// samples is array of objects +[ + {bloodPressureSystolicValue: 120, bloodPressureDiastolicValue: 81, startDate:'2016-06-29T17:55:00.000-0400', endDate:'2016-06-29T17:55:00.000-0400'}, + {bloodPressureSystolicValue: 119, bloodPressureDiastolicValue: 77, startDate:'2016-03-12T13:22:00.000-0400', endDate:'2016-03-12T13:22:00.000-0400'}, + ... +] +``` +*example usage* +```javascript +AppleHealthKit.getBloodPressureSamples(options, (err: Object, samples: Array) => { + if(this._handleHealthKitError(err, 'getBloodPressureSamples')){ + return; + } + // use samples ... +}); +``` + +___ + +#### **`getRespiratoryRateSamples`** +query for respiratory rate samples. the options object is used to setup a query to retrieve relevant samples. +```javascript +let options = { + unit: 'bpm', // optional; default 'bpm' + startDate: (new Date(2016,4,27)).toISOString(), // required + endDate: (new Date()).toISOString(), // optional; default now + ascending: false, // optional; default false + limit:10, // optional; default no limit +}; +``` + +the callback function will be called with a `samples` array containing objects with *value*, *startDate*, and *endDate* fields + +*example usage* +```javascript +AppleHealthKit.getRespiratoryRateSamples(options, (err: Object, samples: Array) => { + if(this._handleHealthKitError(err, 'getRespiratoryRateSamples')){ + return; + } + // use samples ... +}); +``` + +___ + +#### **`getBloodGlucoseSamples`** +query for blood glucose samples. the options object is used to setup a query to retrieve relevant samples. +```javascript +let options = { + unit: 'mmolPerL', // optional; default 'mmolPerL' + startDate: (new Date(2016,4,27)).toISOString(), // required + endDate: (new Date()).toISOString(), // optional; default now + ascending: false, // optional; default false + limit:10, // optional; default no limit +}; +``` +available units are: `'mmolPerL'`, `'mgPerdL'`. + +the callback function will be called with a `samples` array containing objects with *value*, *startDate*, and *endDate* fields + +*example usage* +```javascript +AppleHealthKit.getBloodGlucoseSamples(options, (err: Object, samples: Array) => { + if(this._handleHealthKitError(err, 'getBloodGlucoseSamples')){ + return; + } + // use samples ... +}); +``` + + + + ## Examples #### StepsDemo diff --git a/constants.js b/constants.js index b11b4f04b1f8e3dd6f8b59a1034885af4f8bcb51..1048ef716c60accd31cb11dfc1cfe027a9d4783d 100644 --- a/constants.js +++ b/constants.js @@ -19,6 +19,12 @@ const PERMISSIONS = { NikeFuel: 'NikeFuel', AppleExerciseTime: 'AppleExerciseTime', DietaryEnergy: 'DietaryEnergy', + HeartRate: 'HeartRate', + BodyTemperature: 'BodyTemperature', + BloodPressureSystolic: 'BloodPressureSystolic', + BloodPressureDiastolic: 'BloodPressureDiastolic', + RespiratoryRate: 'RespiratoryRate', + BloodGlucose: 'BloodGlucose', }; @@ -36,7 +42,13 @@ const UNITS = { joule: 'joule', calorie: 'calorie', count: 'count', - percent: 'percent' + percent: 'percent', + bpm: 'bpm', + fahrenheit: 'fahrenheit', + celsius: 'celsius', + mmhg: 'mmhg', + mmolPerL: 'mmolPerL', + mgPerdL: 'mgPerdL', };