Skip to content

Commit aeebb30

Browse files
authored
Merge pull request #321 from agencyenterprise/feature/insulin
Support for Insulin Delivery samples
2 parents 91caa05 + 3d9ef61 commit aeebb30

10 files changed

+259
-1
lines changed

RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.h

+4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111

1212
- (void)results_getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
1313
- (void)results_getCarbohydratesSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
14+
- (void)results_getInsulinDeliverySamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
1415
- (void)results_saveBloodGlucoseSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
1516
- (void)results_saveCarbohydratesSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
1617
- (void)results_deleteBloodGlucoseSample:(NSString *)oid callback:(RCTResponseSenderBlock)callback;
1718
- (void)results_deleteCarbohydratesSample:(NSString *)oid callback:(RCTResponseSenderBlock)callback;
19+
- (void)results_saveInsulinDeliverySample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
20+
- (void)results_deleteInsulinDeliverySample:(NSString *)oid callback:(RCTResponseSenderBlock)callback;
21+
- (void)results_registerObservers:(RCTBridge *)bridge hasListeners:(bool)hasListeners;
1822

1923
@end

RCTAppleHealthKit/RCTAppleHealthKit+Methods_Results.m

+85
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,40 @@ - (void)results_getBloodGlucoseSamples:(NSDictionary *)input callback:(RCTRespon
4646
}];
4747
}
4848

49+
- (void)results_getInsulinDeliverySamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
50+
{
51+
HKQuantityType *insulinDeliveryType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
52+
53+
HKUnit *unit = [HKUnit internationalUnit];
54+
55+
NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit];
56+
BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false];
57+
NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil];
58+
NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]];
59+
if(startDate == nil){
60+
callback(@[RCTMakeError(@"startDate is required in options", nil, nil)]);
61+
return;
62+
}
63+
64+
NSPredicate * predicate = [RCTAppleHealthKit predicateForSamplesBetweenDates:startDate endDate:endDate];
65+
66+
[self fetchQuantitySamplesOfType:insulinDeliveryType
67+
unit:unit
68+
predicate:predicate
69+
ascending:ascending
70+
limit:limit
71+
completion:^(NSArray *results, NSError *error) {
72+
if(results){
73+
callback(@[[NSNull null], results]);
74+
return;
75+
} else {
76+
NSLog(@"An error occured while retrieving the glucose sample %@. The error was: ", error);
77+
callback(@[RCTMakeError(@"An error occured while retrieving the glucose sample", error, nil)]);
78+
return;
79+
}
80+
}];
81+
}
82+
4983
- (void)results_getCarbohydratesSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
5084
{
5185
HKQuantityType *carbohydratesType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryCarbohydrates];
@@ -77,6 +111,34 @@ - (void)results_getCarbohydratesSamples:(NSDictionary *)input callback:(RCTRespo
77111
}];
78112
}
79113

114+
- (void)results_saveInsulinDeliverySample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
115+
{
116+
HKQuantityType *insulinDeliveryType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
117+
118+
HKUnit *unit = [HKUnit internationalUnit];
119+
120+
double value = [RCTAppleHealthKit doubleValueFromOptions:input];
121+
NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:[NSDate date]];
122+
NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:startDate];
123+
NSDictionary *metadata = [RCTAppleHealthKit metadataFromOptions:input withDefault:nil];
124+
125+
HKQuantity *quantity = [HKQuantity quantityWithUnit:unit doubleValue:value];
126+
HKQuantitySample *sample = [HKQuantitySample quantitySampleWithType:insulinDeliveryType
127+
quantity:quantity
128+
startDate:startDate
129+
endDate:endDate
130+
metadata:metadata];
131+
132+
[self.healthStore saveObject:sample withCompletion:^(BOOL success, NSError *error) {
133+
if (!success) {
134+
NSLog(@"An error occured while saving the insulin sample %@. The error was: ", error);
135+
callback(@[RCTMakeError(@"An error occured while saving the insulin sample", error, nil)]);
136+
return;
137+
}
138+
callback(@[[NSNull null], [sample.UUID UUIDString]]);
139+
}];
140+
}
141+
80142
- (void)results_saveBloodGlucoseSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
81143
{
82144
HKQuantityType *bloodGlucoseType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
@@ -164,4 +226,27 @@ - (void)results_deleteCarbohydratesSample:(NSString *)oid callback:(RCTResponseS
164226
}];
165227
}
166228

229+
- (void)results_deleteInsulinDeliverySample:(NSString *)oid callback:(RCTResponseSenderBlock)callback
230+
{
231+
HKQuantityType *insulinDeliveryType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
232+
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:oid];
233+
NSPredicate *uuidPredicate = [HKQuery predicateForObjectWithUUID:uuid];
234+
[self.healthStore deleteObjectsOfType:insulinDeliveryType predicate:uuidPredicate withCompletion:^(BOOL success, NSUInteger deletedObjectCount, NSError * _Nullable error) {
235+
if (!success) {
236+
NSLog(@"An error occured while deleting the insulin delivery sample %@. The error was: ", error);
237+
callback(@[RCTMakeError(@"An error occured while deleting the insulin delivery sample", error, nil)]);
238+
return;
239+
}
240+
callback(@[[NSNull null], @(deletedObjectCount)]);
241+
}];
242+
}
243+
244+
- (void)results_registerObservers:(RCTBridge *)bridge hasListeners:(bool)hasListeners
245+
{
246+
if (@available(iOS 11.0, *)) {
247+
HKSampleType* insulinType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
248+
[self setObserverForType:insulinType type:@"InsulinDelivery" bridge:bridge hasListeners:hasListeners];
249+
}
250+
}
251+
167252
@end

RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m

+4
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ - (nullable HKObjectType *)getReadPermFromText:(nonnull NSString*)key {
158158
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryWater];
159159
} else if ([@"BloodGlucose" isEqualToString:key]) {
160160
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
161+
} else if ([@"InsulinDelivery" isEqualToString:key]) {
162+
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
161163
}
162164

163165
// Vital Signs Identifiers
@@ -355,6 +357,8 @@ - (nullable HKObjectType *)getWritePermFromText:(nonnull NSString*) key {
355357
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryWater];
356358
} else if ([@"BloodGlucose" isEqualToString:key]) {
357359
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
360+
} else if ([@"InsulinDelivery" isEqualToString:key]) {
361+
return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierInsulinDelivery];
358362
}
359363

360364
// Sleep

RCTAppleHealthKit/RCTAppleHealthKit.m

+22-1
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,24 @@ + (BOOL)requiresMainQueueSetup
472472
[self results_getCarbohydratesSamples:input callback:callback];
473473
}
474474

475+
RCT_EXPORT_METHOD(getInsulinDeliverySamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
476+
{
477+
[self _initializeHealthStore];
478+
[self results_getInsulinDeliverySamples:input callback:callback];
479+
}
480+
481+
RCT_EXPORT_METHOD(saveInsulinDeliverySample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
482+
{
483+
[self _initializeHealthStore];
484+
[self results_saveInsulinDeliverySample:input callback:callback];
485+
}
486+
487+
RCT_EXPORT_METHOD(deleteInsulinDeliverySample:(NSString *)oid callback:(RCTResponseSenderBlock)callback)
488+
{
489+
[self _initializeHealthStore];
490+
[self results_deleteInsulinDeliverySample:oid callback:callback];
491+
}
492+
475493
RCT_EXPORT_METHOD(saveCarbohydratesSample:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback)
476494
{
477495
[self _initializeHealthStore];
@@ -668,7 +686,8 @@ - (void)initializeHealthKit:(NSDictionary *)input callback:(RCTResponseSenderBlo
668686
@"MedicationRecord",
669687
@"ProcedureRecord",
670688
@"VitalSignRecord",
671-
@"SleepAnalysis"
689+
@"SleepAnalysis",
690+
@"InsulinDelivery"
672691
];
673692

674693
NSArray *templates = @[@"healthKit:%@:new", @"healthKit:%@:failure", @"healthKit:%@:enabled", @"healthKit:%@:sample", @"healthKit:%@:setup:success", @"healthKit:%@:setup:failure"];
@@ -786,6 +805,8 @@ - (void)initializeBackgroundObservers:(RCTBridge *)bridge
786805
for(NSString * type in clinicalObservers) {
787806
[self clinical_registerObserver:type bridge:bridge hasListeners:hasListeners];
788807
}
808+
809+
[self results_registerObservers:bridge hasListeners:hasListeners];
789810

790811
NSLog(@"[HealthKit] Background observers added to the app");
791812
[self startObserving];

docs/background.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ following:
1111
- `ActiveEnergyBurned`
1212
- `BasalEnergyBurned`
1313
- `Cycling`
14+
- `InsulinDelivery`
1415
- `HeartRate`
1516
- `HeartRateVariabilitySDNN`
1617
- `RestingHeartRate`

docs/deleteInsulinDeliverySample.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# deleteInsulinDeliverySample
2+
3+
Delete a insulin delivery value from HealthKit.
4+
5+
`deleteInsulinDeliverySample` accepts an record's UUID string and a callback:
6+
7+
Example input object:
8+
9+
```javascript
10+
let id = "A11E708A-63A4-42DF-B1E1-F5E2F88B6CA1"
11+
```
12+
13+
Example usage:
14+
15+
```javascript
16+
AppleHealthKit.deleteInsulinDeliverySample(
17+
id,
18+
(err: string, result: HealthValue) => {
19+
if (err) {
20+
console.log(err)
21+
return
22+
}
23+
// sample successfully deleted
24+
console.log(result)
25+
},
26+
)
27+
```
28+
29+
Example output (1 if deleted):
30+
31+
```json
32+
1
33+
```

docs/getInsulinDeliverySamples.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# getInsulinDeliverySamples
2+
3+
Query for insulin delivery samples. The options object is used to setup a query to retrieve relevant samples.
4+
5+
Example input options:
6+
7+
```javascript
8+
let options = {
9+
startDate: new Date(2021, 0, 0).toISOString(), // required
10+
endDate: new Date().toISOString(), // optional; default now
11+
ascending: false, // optional; default false
12+
limit: 10, // optional; default no limit
13+
}
14+
```
15+
16+
Insulin delivery samples are always in International Units.
17+
18+
```javascript
19+
AppleHealthKit.getInsulinDeliverySamples(
20+
options,
21+
(callbackError: string, results: HealthValue[]) => {
22+
console.log(results)
23+
},
24+
);
25+
```
26+
27+
Example output:
28+
29+
```json
30+
[
31+
{
32+
"id": "8DE6A905-02B7-41D2-BB6E-67D1DD82DD6F", // The universally unique identifier (UUID) for this HealthKit object.
33+
"endDate": "2021-03-22T16:19:00.000-0300",
34+
"sourceId": "com.apple.Health",
35+
"sourceName": "Health",
36+
"startDate": "2021-03-22T16:19:00.000-0300",
37+
"value": 5,
38+
"metadata": {
39+
"HKWasUserEntered": true,
40+
"HKInsulinDeliveryReason": 2, // Basal = 1, Bolus = 2
41+
}
42+
}
43+
]
44+
```

docs/saveInsulinDeliverySample.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# saveInsulinDeliverySample
2+
3+
Save a insulin delivery value to HealthKit.
4+
5+
`saveInsulinDeliverySample` accepts an options object containing insulin sample data and a callback:
6+
7+
Example input object:
8+
9+
```javascript
10+
let input = {
11+
value: 10, // IU
12+
startDate: '2021-03-22T16:19:00.000-0300', // Optional, defaults to now
13+
endDate: '2021-03-22T16:19:00.000-0300', // Optional, defaults to startDate
14+
metadata: {
15+
HKBloodGlucoseMealTime: 1, //Basal = 1, Bolus = 2
16+
anyOtherKey: 'some data', // supports string, number, boolean
17+
}
18+
}
19+
```
20+
21+
Insulin delivery samples are always in International Units.
22+
23+
Example usage:
24+
25+
```javascript
26+
AppleHealthKit.saveInsulinDeliverySample(
27+
input,
28+
(err: Object, result: string) => {
29+
if (err) {
30+
return
31+
}
32+
// insulin delivery successfully saved
33+
console.log(result)
34+
},
35+
)
36+
```
37+
38+
Example output (record's UUID):
39+
40+
```json
41+
"619E37D3-C675-4186-B6A4-395EBFC6F46D"
42+
```

index.d.ts

+23
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,22 @@ declare module 'react-native-health' {
437437
callback: (err: string, results: Array<HealthActivitySummary>) => void,
438438
): void
439439

440+
getInsulinDeliverySamples(
441+
options: HealthInputOptions,
442+
callback: (err: string, results: Array<HealthValue>) => void,
443+
): void
444+
445+
saveInsulinDeliverySample(
446+
options: HealthValueOptions,
447+
callback: (err: string, results: HealthValue) => void,
448+
): void
449+
450+
deleteInsulinDeliverySample(
451+
id: string,
452+
callback: (error: string, result: HealthValue) => void,
453+
): void
454+
455+
440456
Constants: Constants
441457
}
442458

@@ -464,6 +480,7 @@ declare module 'react-native-health' {
464480

465481
export interface RecordMetadata {
466482
HKBloodGlucoseMealTime?: BloodGlucoseMealTime
483+
HKInsulinDeliveryReason?: InsulinDeliveryReason
467484
HKWasUserEntered?: boolean
468485
[key: string]: string | number | boolean | undefined
469486
}
@@ -708,6 +725,7 @@ declare module 'react-native-health' {
708725
Folate = 'Folate',
709726
HeadphoneAudioExposure = 'HeadphoneAudioExposure',
710727
ImmunizationRecord = 'ImmunizationRecord',
728+
InsulinDelivery = 'InsulinDelivery',
711729
Iodine = 'Iodine',
712730
Iron = 'Iron',
713731
LabResultRecord = 'LabResultRecord',
@@ -845,6 +863,11 @@ declare module 'react-native-health' {
845863
Postprandial = 2,
846864
}
847865

866+
export enum InsulinDeliveryReason {
867+
Basal = 1,
868+
Bolus = 2,
869+
}
870+
848871
const appleHealthKit: AppleHealthKit
849872

850873
export default appleHealthKit

src/constants/Permissions.js

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const Permissions = {
4040
Folate: 'Folate',
4141
HeadphoneAudioExposure: 'HeadphoneAudioExposure',
4242
ImmunizationRecord: 'ImmunizationRecord',
43+
InsulinDelivery: 'InsulinDelivery',
4344
Iodine: 'Iodine',
4445
Iron: 'Iron',
4546
LabResultRecord: 'LabResultRecord',

0 commit comments

Comments
 (0)