Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Reproductive Health data types #103

Draft
wants to merge 7 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions RCTAppleHealthKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
0F25E9BF2652B419002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F25E9BE2652B419002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.m */; };
358330DF20931B8A000F0B94 /* RCTAppleHealthKit+Methods_Workout.m in Sources */ = {isa = PBXBuildFile; fileRef = 358330DE20931B8A000F0B94 /* RCTAppleHealthKit+Methods_Workout.m */; };
3774C8911D2092F20000B3F3 /* RCTAppleHealthKit.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3774C8901D2092F20000B3F3 /* RCTAppleHealthKit.h */; };
3774C8931D2092F20000B3F3 /* RCTAppleHealthKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 3774C8921D2092F20000B3F3 /* RCTAppleHealthKit.m */; };
Expand Down Expand Up @@ -40,6 +41,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
0F25E9BE2652B419002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RCTAppleHealthKit+Methods_ReproductiveHealth.m"; sourceTree = "<group>"; };
0F25E9C02652B497002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTAppleHealthKit+Methods_ReproductiveHealth.h"; sourceTree = "<group>"; };
27B60163269770030034AB4E /* RCTAppleHealthKit+Methods_Summary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTAppleHealthKit+Methods_Summary.h"; sourceTree = "<group>"; };
27B60164269770850034AB4E /* RCTAppleHealthKit+Methods_Summary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RCTAppleHealthKit+Methods_Summary.m"; sourceTree = "<group>"; };
358330DD20931B8A000F0B94 /* RCTAppleHealthKit+Methods_Workout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTAppleHealthKit+Methods_Workout.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -135,6 +138,8 @@
37837E7C1DCFE270000201A0 /* RCTAppleHealthKit+Methods_Sleep.m */,
358330DD20931B8A000F0B94 /* RCTAppleHealthKit+Methods_Workout.h */,
358330DE20931B8A000F0B94 /* RCTAppleHealthKit+Methods_Workout.m */,
0F25E9BE2652B419002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.m */,
0F25E9C02652B497002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.h */,
8C640F3E269DF680004CAFED /* RCTAppleHealthKit+Methods_Hearing.h */,
8C640F3F269DF69C004CAFED /* RCTAppleHealthKit+Methods_Hearing.m */,
27B60163269770030034AB4E /* RCTAppleHealthKit+Methods_Summary.h */,
Expand Down Expand Up @@ -205,6 +210,7 @@
3774C89B1D2095450000B3F3 /* RCTAppleHealthKit+Queries.m in Sources */,
3774C8A11D20A6B90000B3F3 /* RCTAppleHealthKit+Utils.m in Sources */,
8C640F40269DF69C004CAFED /* RCTAppleHealthKit+Methods_Hearing.m in Sources */,
0F25E9BF2652B419002EB8E1 /* RCTAppleHealthKit+Methods_ReproductiveHealth.m in Sources */,
37837E7D1DCFE270000201A0 /* RCTAppleHealthKit+Methods_Sleep.m in Sources */,
3774C8D41D20C6390000B3F3 /* RCTAppleHealthKit+Methods_Body.m in Sources */,
61232F931E303758000A5026 /* RCTAppleHealthKit+Methods_Mindfulness.m in Sources */,
Expand Down
13 changes: 13 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_ReproductiveHealth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// RCTAppleHealthKit+Methods_ReproductiveHealth.h
// RCTAppleHealthKit
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.

#import "RCTAppleHealthKit.h"

@interface RCTAppleHealthKit (Methods_ReproductiveHealth)
- (void)reproductiveHealth_getBasalBodyTemperatureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)reproductiveHealth_getMenstrualFlowSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
@end
74 changes: 74 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_ReproductiveHealth.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// RCTAppleHealthKit+Methods_ReproductiveHealth.m
// RCTAppleHealthKit
//
// Created by Ivanka Todorova on 17/05/2021.

// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.

#import "RCTAppleHealthKit+Methods_ReproductiveHealth.h"
#import "RCTAppleHealthKit+Queries.h"
#import "RCTAppleHealthKit+Utils.h"

@implementation RCTAppleHealthKit (Methods_ReproductiveHealth)

- (void)reproductiveHealth_getBasalBodyTemperatureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *basalBodyTemperatureType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBasalBodyTemperature];
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:basalBodyTemperatureType
unit:unit
predicate:predicate
ascending:ascending
limit:limit
completion:^(NSArray *results, NSError *error) {
if(results){
callback(@[[NSNull null], results]);
return;
} else {
NSLog(@"An error occured while retrieving the basal body temperature sample %@. The error was: ", error);
callback(@[RCTMakeError(@"An error occured while retrieving the basal body temperature sample", error, nil)]);
return;
}
}];
}


- (void)reproductiveHealth_getMenstrualFlowSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
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];
NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit];


[self fetchMenstrualFlowSamplesForPredicate:predicate
limit:limit
completion:^(NSArray *results, NSError *error) {
if(results){
callback(@[[NSNull null], results]);
return;
} else {
callback(@[RCTJSErrorFromNSError(error)]);
return;
}
}];

}
@end
3 changes: 3 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Queries.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@
limit:(NSUInteger)lim
ascending:(BOOL)asc
completion:(void (^)(NSArray *, NSError *))completion;
- (void)fetchMenstrualFlowSamplesForPredicate:(NSPredicate *)predicate
limit:(NSUInteger)lim
completion:(void (^)(NSArray *, NSError *))completion;

- (void)fetchWorkoutForPredicate:(NSPredicate *)predicate
ascending:(BOOL)ascending
Expand Down
78 changes: 78 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Queries.m
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,84 @@ - (void)fetchSleepCategorySamplesForPredicate:(NSPredicate *)predicate
[self.healthStore executeQuery:query];
}

- (void)fetchMenstrualFlowSamplesForPredicate:(NSPredicate *)predicate
limit:(NSUInteger)lim
completion:(void (^)(NSArray *, NSError *))completion {

NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate
ascending:false];


// 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 (HKCategorySample *sample in results) {
NSInteger val = sample.value;

NSString *startDateString = [RCTAppleHealthKit buildISO8601StringFromDate:sample.startDate];
NSString *endDateString = [RCTAppleHealthKit buildISO8601StringFromDate:sample.endDate];

NSString *valueString;

switch (val) {
case HKCategoryValueMenstrualFlowNone:
valueString = @"NONE";
break;
case HKCategoryValueMenstrualFlowLight:
valueString = @"LIGHT";
break;
case HKCategoryValueMenstrualFlowMedium:
valueString = @"MEDIUM";
break;
case HKCategoryValueMenstrualFlowHeavy:
valueString = @"HEAVY";
break;
default:
valueString = @"UNSPECIFIED";
break;
}

NSDictionary *elem = @{
@"value" : valueString,
@"startDate" : startDateString,
@"endDate" : endDateString,
@"sourceName" : [[[sample sourceRevision] source] name],
@"sourceId" : [[[sample sourceRevision] source] bundleIdentifier],
};

[data addObject:elem];
}

completion(data, error);
});
}
};

HKCategoryType *categoryType =
[HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierMenstrualFlow];

HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:categoryType
predicate:predicate
limit:lim
sortDescriptors:@[timeSortDescriptor]
resultsHandler:handlerBlock];

[self.healthStore executeQuery:query];
}

- (void)fetchCorrelationSamplesOfType:(HKQuantityType *)quantityType
unit:(HKUnit *)unit
predicate:(NSPredicate *)predicate
Expand Down
42 changes: 42 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,27 @@ - (nullable HKObjectType *)getReadPermFromText:(nonnull NSString*)key {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
}

// Reproductive Health identifiers
if([@"BasalBodyTemperature" isEqualToString:key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBasalBodyTemperature];
} else if ([@"CervicalMucusQuality" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierCervicalMucusQuality];
} else if ([@"Contraceptive" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierContraceptive];
} else if ([@"IntermenstrualBleeding" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierIntermenstrualBleeding];
} else if ([@"Lactation" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierLactation];
} else if ([@"OvulationTestResult" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierOvulationTestResult];
} else if ([@"Pregnancy" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierPregnancy];
} else if ([@"SexualActivity" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSexualActivity];
} else if ([@"MenstrualFlow" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierMenstrualFlow];
}

// Vital Signs Identifiers
if ([@"HeartRate" isEqualToString: key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
Expand Down Expand Up @@ -351,6 +372,27 @@ - (nullable HKObjectType *)getWritePermFromText:(nonnull NSString*) key {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
}

// Reproductive Health identifiers
if([@"BasalBodyTemperature" isEqualToString:key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBasalBodyTemperature];
} else if ([@"CervicalMucusQuality" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierCervicalMucusQuality];
} else if ([@"Contraceptive" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierContraceptive];
} else if ([@"IntermenstrualBleeding" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierIntermenstrualBleeding];
} else if ([@"Lactation" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierLactation];
} else if ([@"OvulationTestResult" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierOvulationTestResult];
} else if ([@"Pregnancy" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierPregnancy];
} else if ([@"SexualActivity" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSexualActivity];
} else if ([@"MenstrualFlow" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierMenstrualFlow];
}

// Sleep
if ([@"SleepAnalysis" isEqualToString:key]) {
return [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];
Expand Down
20 changes: 15 additions & 5 deletions RCTAppleHealthKit/RCTAppleHealthKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "RCTAppleHealthKit+Methods_Hearing.h"
#import "RCTAppleHealthKit+Methods_Summary.h"
#import "RCTAppleHealthKit+Methods_ClinicalRecords.h"
#import "RCTAppleHealthKit+Methods_ReproductiveHealth.h"

#import <React/RCTBridgeModule.h>
#import <React/RCTEventDispatcher.h>
Expand Down Expand Up @@ -299,6 +300,15 @@ + (BOOL)requiresMainQueueSetup
{
[self dietary_getTotalFatSamples:input callback:callback];
}
RCT_EXPORT_METHOD(getBasalBodyTemperatureSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
{
[self reproductiveHealth_getBasalBodyTemperatureSamples:input callback:callback];
}
RCT_EXPORT_METHOD(getMenstrualFlowSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
{
[self reproductiveHealth_getMenstrualFlowSamples:input callback:callback];
}


RCT_EXPORT_METHOD(saveFood:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
{
Expand Down Expand Up @@ -623,9 +633,9 @@ - (void)initializeHealthKit:(NSDictionary *)input callback:(RCTResponseSenderBlo
@"VitalSignRecord",
@"SleepAnalysis"
];

NSArray *templates = @[@"healthKit:%@:new", @"healthKit:%@:failure", @"healthKit:%@:enabled", @"healthKit:%@:sample", @"healthKit:%@:setup:success", @"healthKit:%@:setup:failure"];

NSMutableArray *supportedEvents = [[NSMutableArray alloc] init];

for(NSString * type in types) {
Expand All @@ -651,7 +661,7 @@ - (void)getModuleInfo:(NSDictionary *)input callback:(RCTResponseSenderBlock)cal

- (void)getAuthorizationStatus:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{

[self _initializeHealthStore];
if ([HKHealthStore isHealthDataAvailable]) {

Expand Down Expand Up @@ -726,7 +736,7 @@ - (void)initializeBackgroundObservers:(RCTBridge *)bridge
for(NSString * type in fitnessObservers) {
[self fitness_registerObserver:type bridge:bridge hasListeners:hasListeners];
}

NSArray *clinicalObservers = @[
@"AllergyRecord",
@"ConditionRecord",
Expand All @@ -737,7 +747,7 @@ - (void)initializeBackgroundObservers:(RCTBridge *)bridge
@"ProcedureRecord",
@"VitalSignRecord"
];

for(NSString * type in clinicalObservers) {
[self clinical_registerObserver:type bridge:bridge hasListeners:hasListeners];
}
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ they are splitted in the following categories
- [saveWater](/docs/saveWater.md)
- [getWater](/docs/getWater.md)

### Reproductive Health Methods
- [getBasalBodyTemperatureSamples](/docs/getBasalBodyTemperatureSamples.md)
- [getMenstrualFlowSamples](/docs/getMenstrualFlowSamples.md)

### Fitness Methods

- [getDailyStepCountSamples](/docs/getDailyStepCountSamples.md)
Expand Down
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ There is a gitbook version for the documentation on this [link](https://vinicius
- [getWater](getWater.md)
- [getTotalFatSamples](getTotalFatSamples.md)

### Reproductive Health Methods
- [getBasalBodyTemperatureSamples](getBasalBodyTemperatureSamples.md)
- [getMenstrualFlowSamples](getMenstrualFlowSamples.md)

### Fitness Methods

- [getDailyStepCountSamples](getDailyStepCountSamples.md)
Expand Down
Loading