Skip to content

Commit

Permalink
Merge pull request #321 from agencyenterprise/feature/insulin
Browse files Browse the repository at this point in the history
Support for Insulin Delivery samples
  • Loading branch information
RuanAzevedo committed Jun 30, 2023
2 parents 91caa05 + 3d9ef61 commit aeebb30
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 1 deletion.
4 changes: 4 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@

- (void)results_getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)results_getCarbohydratesSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)results_getInsulinDeliverySamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)results_saveBloodGlucoseSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)results_saveCarbohydratesSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)results_deleteBloodGlucoseSample:(NSString *)oid callback:(RCTResponseSenderBlock)callback;
- (void)results_deleteCarbohydratesSample:(NSString *)oid callback:(RCTResponseSenderBlock)callback;
- (void)results_saveInsulinDeliverySample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)results_deleteInsulinDeliverySample:(NSString *)oid callback:(RCTResponseSenderBlock)callback;
- (void)results_registerObservers:(RCTBridge *)bridge hasListeners:(bool)hasListeners;

@end
85 changes: 85 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,40 @@ - (void)results_getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTRespon
}];
}

- (void)results_getInsulinDeliverySamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *insulinDeliveryType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];

HKUnit *unit = [HKUnit internationalUnit];

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:insulinDeliveryType
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 glucose sample %@. The error was: ", error);
callback(@[RCTMakeError(@"An error occured while retrieving the glucose sample", error, nil)]);
return;
}
}];
}

- (void)results_getCarbohydratesSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *carbohydratesType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryCarbohydrates];
Expand Down Expand Up @@ -77,6 +111,34 @@ - (void)results_getCarbohydratesSamples:(NSDictionary *)input callback:(RCTRespo
}];
}

- (void)results_saveInsulinDeliverySample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *insulinDeliveryType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];

HKUnit *unit = [HKUnit internationalUnit];

double value = [RCTAppleHealthKit doubleValueFromOptions:input];
NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:[NSDate date]];
NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:startDate];
NSDictionary *metadata = [RCTAppleHealthKit metadataFromOptions:input withDefault:nil];

HKQuantity *quantity = [HKQuantity quantityWithUnit:unit doubleValue:value];
HKQuantitySample *sample = [HKQuantitySample quantitySampleWithType:insulinDeliveryType
quantity:quantity
startDate:startDate
endDate:endDate
metadata:metadata];

[self.healthStore saveObject:sample withCompletion:^(BOOL success, NSError *error) {
if (!success) {
NSLog(@"An error occured while saving the insulin sample %@. The error was: ", error);
callback(@[RCTMakeError(@"An error occured while saving the insulin sample", error, nil)]);
return;
}
callback(@[[NSNull null], [sample.UUID UUIDString]]);
}];
}

- (void)results_saveBloodGlucoseSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *bloodGlucoseType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
Expand Down Expand Up @@ -164,4 +226,27 @@ - (void)results_deleteCarbohydratesSample:(NSString *)oid callback:(RCTResponseS
}];
}

- (void)results_deleteInsulinDeliverySample:(NSString *)oid callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *insulinDeliveryType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:oid];
NSPredicate *uuidPredicate = [HKQuery predicateForObjectWithUUID:uuid];
[self.healthStore deleteObjectsOfType:insulinDeliveryType predicate:uuidPredicate withCompletion:^(BOOL success, NSUInteger deletedObjectCount, NSError * _Nullable error) {
if (!success) {
NSLog(@"An error occured while deleting the insulin delivery sample %@. The error was: ", error);
callback(@[RCTMakeError(@"An error occured while deleting the insulin delivery sample", error, nil)]);
return;
}
callback(@[[NSNull null], @(deletedObjectCount)]);
}];
}

- (void)results_registerObservers:(RCTBridge *)bridge hasListeners:(bool)hasListeners
{
if (@available(iOS 11.0, *)) {
HKSampleType* insulinType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
[self setObserverForType:insulinType type:@"InsulinDelivery" bridge:bridge hasListeners:hasListeners];
}
}

@end
4 changes: 4 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ - (nullable HKObjectType *)getReadPermFromText:(nonnull NSString*)key {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryWater];
} else if ([@"BloodGlucose" isEqualToString:key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
} else if ([@"InsulinDelivery" isEqualToString:key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
}

// Vital Signs Identifiers
Expand Down Expand Up @@ -355,6 +357,8 @@ - (nullable HKObjectType *)getWritePermFromText:(nonnull NSString*) key {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryWater];
} else if ([@"BloodGlucose" isEqualToString:key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
} else if ([@"InsulinDelivery" isEqualToString:key]) {
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
}

// Sleep
Expand Down
23 changes: 22 additions & 1 deletion RCTAppleHealthKit/RCTAppleHealthKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,24 @@ + (BOOL)requiresMainQueueSetup
[self results_getCarbohydratesSamples:input callback:callback];
}

RCT_EXPORT_METHOD(getInsulinDeliverySamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
{
[self _initializeHealthStore];
[self results_getInsulinDeliverySamples:input callback:callback];
}

RCT_EXPORT_METHOD(saveInsulinDeliverySample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
{
[self _initializeHealthStore];
[self results_saveInsulinDeliverySample:input callback:callback];
}

RCT_EXPORT_METHOD(deleteInsulinDeliverySample:(NSString *)oid callback:(RCTResponseSenderBlock)callback)
{
[self _initializeHealthStore];
[self results_deleteInsulinDeliverySample:oid callback:callback];
}

RCT_EXPORT_METHOD(saveCarbohydratesSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
{
[self _initializeHealthStore];
Expand Down Expand Up @@ -668,7 +686,8 @@ - (void)initializeHealthKit:(NSDictionary *)input callback:(RCTResponseSenderBlo
@"MedicationRecord",
@"ProcedureRecord",
@"VitalSignRecord",
@"SleepAnalysis"
@"SleepAnalysis",
@"InsulinDelivery"
];

NSArray *templates = @[@"healthKit:%@:new", @"healthKit:%@:failure", @"healthKit:%@:enabled", @"healthKit:%@:sample", @"healthKit:%@:setup:success", @"healthKit:%@:setup:failure"];
Expand Down Expand Up @@ -786,6 +805,8 @@ - (void)initializeBackgroundObservers:(RCTBridge *)bridge
for(NSString * type in clinicalObservers) {
[self clinical_registerObserver:type bridge:bridge hasListeners:hasListeners];
}

[self results_registerObservers:bridge hasListeners:hasListeners];

NSLog(@"[HealthKit] Background observers added to the app");
[self startObserving];
Expand Down
1 change: 1 addition & 0 deletions docs/background.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ following:
- `ActiveEnergyBurned`
- `BasalEnergyBurned`
- `Cycling`
- `InsulinDelivery`
- `HeartRate`
- `HeartRateVariabilitySDNN`
- `RestingHeartRate`
Expand Down
33 changes: 33 additions & 0 deletions docs/deleteInsulinDeliverySample.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# deleteInsulinDeliverySample

Delete a insulin delivery value from HealthKit.

`deleteInsulinDeliverySample` accepts an record's UUID string and a callback:

Example input object:

```javascript
let id = "A11E708A-63A4-42DF-B1E1-F5E2F88B6CA1"
```

Example usage:

```javascript
AppleHealthKit.deleteInsulinDeliverySample(
id,
(err: string, result: HealthValue) => {
if (err) {
console.log(err)
return
}
// sample successfully deleted
console.log(result)
},
)
```

Example output (1 if deleted):

```json
1
```
44 changes: 44 additions & 0 deletions docs/getInsulinDeliverySamples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# getInsulinDeliverySamples

Query for insulin delivery samples. The options object is used to setup a query to retrieve relevant samples.

Example input options:

```javascript
let options = {
startDate: new Date(2021, 0, 0).toISOString(), // required
endDate: new Date().toISOString(), // optional; default now
ascending: false, // optional; default false
limit: 10, // optional; default no limit
}
```

Insulin delivery samples are always in International Units.

```javascript
AppleHealthKit.getInsulinDeliverySamples(
options,
(callbackError: string, results: HealthValue[]) => {
console.log(results)
},
);
```

Example output:

```json
[
{
"id": "8DE6A905-02B7-41D2-BB6E-67D1DD82DD6F", // The universally unique identifier (UUID) for this HealthKit object.
"endDate": "2021-03-22T16:19:00.000-0300",
"sourceId": "com.apple.Health",
"sourceName": "Health",
"startDate": "2021-03-22T16:19:00.000-0300",
"value": 5,
"metadata": {
"HKWasUserEntered": true,
"HKInsulinDeliveryReason": 2, // Basal = 1, Bolus = 2
}
}
]
```
42 changes: 42 additions & 0 deletions docs/saveInsulinDeliverySample.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# saveInsulinDeliverySample

Save a insulin delivery value to HealthKit.

`saveInsulinDeliverySample` accepts an options object containing insulin sample data and a callback:

Example input object:

```javascript
let input = {
value: 10, // IU
startDate: '2021-03-22T16:19:00.000-0300', // Optional, defaults to now
endDate: '2021-03-22T16:19:00.000-0300', // Optional, defaults to startDate
metadata: {
HKBloodGlucoseMealTime: 1, //Basal = 1, Bolus = 2
anyOtherKey: 'some data', // supports string, number, boolean
}
}
```

Insulin delivery samples are always in International Units.

Example usage:

```javascript
AppleHealthKit.saveInsulinDeliverySample(
input,
(err: Object, result: string) => {
if (err) {
return
}
// insulin delivery successfully saved
console.log(result)
},
)
```

Example output (record's UUID):

```json
"619E37D3-C675-4186-B6A4-395EBFC6F46D"
```
23 changes: 23 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,22 @@ declare module 'react-native-health' {
callback: (err: string, results: Array<HealthActivitySummary>) => void,
): void

getInsulinDeliverySamples(
options: HealthInputOptions,
callback: (err: string, results: Array<HealthValue>) => void,
): void

saveInsulinDeliverySample(
options: HealthValueOptions,
callback: (err: string, results: HealthValue) => void,
): void

deleteInsulinDeliverySample(
id: string,
callback: (error: string, result: HealthValue) => void,
): void


Constants: Constants
}

Expand Down Expand Up @@ -464,6 +480,7 @@ declare module 'react-native-health' {

export interface RecordMetadata {
HKBloodGlucoseMealTime?: BloodGlucoseMealTime
HKInsulinDeliveryReason?: InsulinDeliveryReason
HKWasUserEntered?: boolean
[key: string]: string | number | boolean | undefined
}
Expand Down Expand Up @@ -708,6 +725,7 @@ declare module 'react-native-health' {
Folate = 'Folate',
HeadphoneAudioExposure = 'HeadphoneAudioExposure',
ImmunizationRecord = 'ImmunizationRecord',
InsulinDelivery = 'InsulinDelivery',
Iodine = 'Iodine',
Iron = 'Iron',
LabResultRecord = 'LabResultRecord',
Expand Down Expand Up @@ -845,6 +863,11 @@ declare module 'react-native-health' {
Postprandial = 2,
}

export enum InsulinDeliveryReason {
Basal = 1,
Bolus = 2,
}

const appleHealthKit: AppleHealthKit

export default appleHealthKit
Expand Down
1 change: 1 addition & 0 deletions src/constants/Permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const Permissions = {
Folate: 'Folate',
HeadphoneAudioExposure: 'HeadphoneAudioExposure',
ImmunizationRecord: 'ImmunizationRecord',
InsulinDelivery: 'InsulinDelivery',
Iodine: 'Iodine',
Iron: 'Iron',
LabResultRecord: 'LabResultRecord',
Expand Down

0 comments on commit aeebb30

Please sign in to comment.