diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h index 6a416ef4..c33c470c 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h @@ -25,6 +25,7 @@ - (void)fitness_getFlightsClimbedOnDay:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; - (void)fitness_getDailyFlightsClimbedSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; +- (void)fitness_getAnchoredQuery:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; - (void)fitness_setObserver:(NSDictionary *)input __deprecated; - (void)fitness_registerObserver:(NSString *)type bridge:(RCTBridge *)bridge diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m index a83a441b..b73c4782 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m @@ -439,6 +439,39 @@ - (void)fitness_getDailyFlightsClimbedSamples:(NSDictionary *)input callback:(RC }]; } +- (void)fitness_getAnchoredQuery:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback +{ + NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit]; + + HKSampleType *stepType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; + HKQueryAnchor *anchor = [RCTAppleHealthKit hkAnchorFromOptions:input]; + NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:nil]; + NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]]; + + NSPredicate *predicate = [RCTAppleHealthKit predicateForAnchoredQueries:anchor startDate:startDate endDate:endDate]; + + void (^completion)(NSDictionary *results, NSError *error); + + completion = ^(NSDictionary *results, NSError *error) { + if (results){ + callback(@[[NSNull null], results]); + + return; + } else { + NSLog(@"error getting samples: %@", error); + callback(@[RCTMakeError(@"error getting samples", error, nil)]); + + return; + } + }; + + [self fetchAnchoredStepCount:stepType + predicate:predicate + anchor:anchor + limit:limit + completion:completion]; +} + /*! Register observer from React Native object diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h index 3ff69c75..2f3dc739 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h @@ -64,6 +64,12 @@ limit:(NSUInteger)lim completion:(void (^)(NSDictionary *, NSError *))completion; +- (void)fetchAnchoredStepCount:(HKSampleType *)type + predicate:(NSPredicate *)predicate + anchor:(HKQueryAnchor *)anchor + limit:(NSUInteger)lim + completion:(void (^)(NSDictionary *, NSError *))completion; + - (void)fetchQuantitySamplesOfType:(HKQuantityType *)quantityType unit:(HKUnit *)unit predicate:(NSPredicate *)predicate diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m index fbc07d87..15d2884f 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m @@ -578,6 +578,69 @@ - (void)fetchAnchoredWorkouts:(HKSampleType *)type [self.healthStore executeQuery:query]; } +- (void)fetchAnchoredStepCount:(HKSampleType *)type + predicate:(NSPredicate *)predicate + anchor:(HKQueryAnchor *)anchor + limit:(NSUInteger)lim + completion:(void (^)(NSDictionary *, NSError *))completion { + + // declare the block + void (^handlerBlock)(HKAnchoredObjectQuery *query, NSArray<__kindof HKSample *> *sampleObjects, NSArray *deletedObjects, HKQueryAnchor *newAnchor, NSError *error); + + // create and assign the block + handlerBlock = ^(HKAnchoredObjectQuery *query, NSArray<__kindof HKSample *> *sampleObjects, NSArray *deletedObjects, HKQueryAnchor *newAnchor, NSError *error) { + + if (!sampleObjects) { + if (completion) { + completion(nil, error); + } + return; + } + + if (completion) { + NSMutableArray *data = [NSMutableArray arrayWithCapacity:1]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + for (HKQuantitySample *sample in sampleObjects) { + @try { + double stepCount = [sample.quantity doubleValueForUnit:[HKUnit countUnit]]; + + NSString *startDateString = [RCTAppleHealthKit buildISO8601StringFromDate:sample.startDate]; + NSString *endDateString = [RCTAppleHealthKit buildISO8601StringFromDate:sample.endDate]; + + NSDictionary *elem = @{ + @"id" : [[sample UUID] UUIDString], + @"startDate" : startDateString, + @"endDate" : endDateString, + @"value" : @(stepCount), + @"sourceName" : [[[sample sourceRevision] source] name], + }; + + [data addObject:elem]; + } @catch (NSException *exception) { + NSLog(@"RNHealth: An error occured while trying to add step count sample from: %@ ", [[[sample sourceRevision] source] bundleIdentifier]); + } + } + + NSData *anchorData = [NSKeyedArchiver archivedDataWithRootObject:newAnchor]; + NSString *anchorString = [anchorData base64EncodedStringWithOptions:0]; + completion(@{ + @"anchor": anchorString, + @"data": data, + }, error); + }); + } + }; + + HKAnchoredObjectQuery *query = [[HKAnchoredObjectQuery alloc] initWithType:type + predicate:predicate + anchor:anchor + limit:lim + resultsHandler:handlerBlock]; + + [self.healthStore executeQuery:query]; +} + - (void)fetchSleepCategorySamplesForPredicate:(NSPredicate *)predicate limit:(NSUInteger)lim ascending:(BOOL)asc diff --git a/RCTAppleHealthKit/RCTAppleHealthKit.m b/RCTAppleHealthKit/RCTAppleHealthKit.m index e8ddce17..de73f5cc 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit.m @@ -265,6 +265,12 @@ + (BOOL)requiresMainQueueSetup [self fitness_getDailyStepSamples:input callback:callback]; } +RCT_EXPORT_METHOD(getAnchoredStepCount:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self _initializeHealthStore]; + [self fitness_getAnchoredQuery:input callback:callback]; +} + RCT_EXPORT_METHOD(saveSteps:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) { [self _initializeHealthStore]; diff --git a/index.d.ts b/index.d.ts index 0cf0e280..6bec9e4d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -155,6 +155,14 @@ declare module 'react-native-health' { callback: (err: HKErrorResponse, results: AnchoredQueryResults) => void, ): void + getAnchoredStepCount( + options: HealthInputOptions, + callback: ( + err: HKErrorResponse, + results: AnchoredStepCountQueryResults, + ) => void, + ): void + getDailyStepCountSamples( options: HealthInputOptions, callback: (err: string, results: Array) => void, @@ -462,7 +470,6 @@ declare module 'react-native-health' { callback: (error: string, result: HealthValue) => void, ): void - Constants: Constants } @@ -499,6 +506,11 @@ declare module 'react-native-health' { value: number } + export interface HealthValueWithSource extends BaseValue { + value: number + sourceName: string + } + export interface BloodPressureSampleValue extends BaseValue { bloodPressureSystolicValue: number bloodPressureDiastolicValue: number @@ -546,7 +558,7 @@ declare module 'react-native-health' { PausedOrResumeRequest = 'pause or resume request', Lap = 'lap', Segment = 'segment', - Marker = 'marker' + Marker = 'marker', } export type HKWorkoutEventType = { @@ -857,6 +869,11 @@ declare module 'react-native-health' { data: Array } + export interface AnchoredStepCountQueryResults { + anchor: string + data: Array + } + export interface WorkoutRouteQueryResults { anchor: string data: HKWorkoutRouteSampleType