Skip to content

Commit

Permalink
Merge pull request #25 from EJohnF/observer
Browse files Browse the repository at this point in the history
Add unified way to get workouts with activity name/id, isTracked flag. Add listener for any changes
  • Loading branch information
EJohnF committed Feb 26, 2019
2 parents 3a47aef + 181f4fa commit 715a0eb
Show file tree
Hide file tree
Showing 17 changed files with 510 additions and 102 deletions.
3 changes: 2 additions & 1 deletion Constants/Permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ export const Permissions = {
SleepAnalysis: "SleepAnalysis",
StepCount: "StepCount",
Steps: "Steps",
Weight: "Weight"
Weight: "Weight",
Workout: "Workout"
}
4 changes: 3 additions & 1 deletion RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// RCTAppleHealthKit
//
// Created by Alexander Vallorosi on 4/27/17.
// Copyright © 2017 Alexander Vallorosi. All rights reserved.
// 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_Activity)
Expand Down
4 changes: 2 additions & 2 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
// RCTAppleHealthKit
//
// Created by Alexander Vallorosi on 4/27/17.
// Copyright © 2017 Alexander Vallorosi. All rights reserved.
//
// 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_Activity.h"
#import "RCTAppleHealthKit+Queries.h"
Expand Down
20 changes: 4 additions & 16 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_Body.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ - (void)body_getLatestWeight:(NSDictionary *)input callback:(RCTResponseSenderBl
{
HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];

HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input];
if(unit == nil){
unit = [HKUnit poundUnit];
}

HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit poundUnit]];

[self fetchMostRecentQuantitySampleOfType:weightType
predicate:nil
completion:^(HKQuantity *mostRecentQuantity, NSDate *startDate, NSDate *endDate, NSError *error) {
Expand All @@ -33,7 +30,6 @@ - (void)body_getLatestWeight:(NSDictionary *)input callback:(RCTResponseSenderBl
else {
// Determine the weight in the required unit.
double usersWeight = [mostRecentQuantity doubleValueForUnit:unit];

NSDictionary *response = @{
@"value" : @(usersWeight),
@"startDate" : [RCTAppleHealthKit buildISO8601StringFromDate:startDate],
Expand Down Expand Up @@ -152,11 +148,7 @@ - (void)body_saveBodyMassIndex:(NSDictionary *)input callback:(RCTResponseSender
- (void)body_getLatestHeight:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];

HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input];
if(unit == nil){
unit = [HKUnit inchUnit];
}
HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit inchUnit]];;

[self fetchMostRecentQuantitySampleOfType:heightType
predicate:nil
Expand Down Expand Up @@ -218,11 +210,7 @@ - (void)body_saveHeight:(NSDictionary *)input callback:(RCTResponseSenderBlock)c
{
double height = [RCTAppleHealthKit doubleValueFromOptions:input];
NSDate *sampleDate = [RCTAppleHealthKit dateFromOptionsDefaultNow:input];

HKUnit *heightUnit = [RCTAppleHealthKit hkUnitFromOptions:input];
if(heightUnit == nil){
heightUnit = [HKUnit inchUnit];
}
HKUnit *heightUnit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit inchUnit]];

HKQuantity *heightQuantity = [HKQuantity quantityWithUnit:heightUnit doubleValue:height];
HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
Expand Down
2 changes: 2 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
@interface RCTAppleHealthKit (Methods_Fitness)

- (void)fitness_getStepCountOnDay:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)fitness_getSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)fitness_setObserver:(NSDictionary *)input;
- (void)fitness_getDailyStepSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)fitness_saveSteps:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
- (void)fitness_initializeStepEventObserver:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback;
Expand Down
46 changes: 46 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,52 @@ - (void)fitness_getStepCountOnDay:(NSDictionary *)input callback:(RCTResponseSen
}];
}

- (void)fitness_getSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit countUnit]];
NSUInteger limit = [RCTAppleHealthKit uintFromOptions:input key:@"limit" withDefault:HKObjectQueryNoLimit];
BOOL ascending = [RCTAppleHealthKit boolFromOptions:input key:@"ascending" withDefault:false];
NSString *type = [RCTAppleHealthKit stringFromOptions:input key:@"type" withDefault:@"Walking"];
NSDate *startDate = [RCTAppleHealthKit dateFromOptions:input key:@"startDate" withDefault:[NSDate date]];
NSDate *endDate = [RCTAppleHealthKit dateFromOptions:input key:@"endDate" withDefault:[NSDate date]];

NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];

HKSampleType *samplesType = [RCTAppleHealthKit hkQuantityTypeFromString:type];
if ([type isEqual:@"Running"] || [type isEqual:@"Cycling"]) {
unit = [HKUnit mileUnit];
}
NSLog(@"error getting samples: %@", [samplesType identifier]);
[self fetchSamplesOfType:samplesType
unit:unit
predicate:predicate
ascending:ascending
limit:limit
completion:^(NSArray *results, NSError *error) {
if(results){
callback(@[[NSNull null], results]);
return;
} else {
NSLog(@"error getting samples: %@", error);
callback(@[RCTMakeError(@"error getting samples", nil, nil)]);
return;
}
}];
}

- (void)fitness_setObserver:(NSDictionary *)input
{
HKUnit *unit = [RCTAppleHealthKit hkUnitFromOptions:input key:@"unit" withDefault:[HKUnit countUnit]];
NSString *type = [RCTAppleHealthKit stringFromOptions:input key:@"type" withDefault:@"Walking"];

HKSampleType *samplesType = [RCTAppleHealthKit hkQuantityTypeFromString:type];
if ([type isEqual:@"Running"] || [type isEqual:@"Cycling"]) {
unit = [HKUnit mileUnit];
}

[self setObserverForType:samplesType unit:unit];
}


- (void)fitness_getDailyStepSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback
{
Expand Down
12 changes: 12 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+Queries.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
startDate:(NSDate *)startDate
endDate:(NSDate *)endDate
completion:(void (^)(NSArray *, NSError *))completionHandler;


- (void)fetchSamplesOfType:(HKSampleType *)quantityType
unit:(HKUnit *)unit
predicate:(NSPredicate *)predicate
ascending:(BOOL)asc
limit:(NSUInteger)lim
completion:(void (^)(NSArray *, NSError *))completion;
- (void)setObserverForType:(HKSampleType *)quantityType
unit:(HKUnit *)unit;


- (void)fetchQuantitySamplesOfType:(HKQuantityType *)quantityType
unit:(HKUnit *)unit
predicate:(NSPredicate *)predicate
Expand Down
153 changes: 144 additions & 9 deletions RCTAppleHealthKit/RCTAppleHealthKit+Queries.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
#import "RCTAppleHealthKit+Queries.h"
#import "RCTAppleHealthKit+Utils.h"

@implementation RCTAppleHealthKit (Queries)
#import <React/RCTBridgeModule.h>
#import <React/RCTEventDispatcher.h>

@implementation RCTAppleHealthKit (Queries)

- (void)fetchMostRecentQuantitySampleOfType:(HKQuantityType *)quantityType
predicate:(NSPredicate *)predicate
Expand Down Expand Up @@ -49,7 +51,6 @@ - (void)fetchMostRecentQuantitySampleOfType:(HKQuantityType *)quantityType
[self.healthStore executeQuery:query];
}


- (void)fetchQuantitySamplesOfType:(HKQuantityType *)quantityType
unit:(HKUnit *)unit
predicate:(NSPredicate *)predicate
Expand Down Expand Up @@ -106,19 +107,153 @@ - (void)fetchQuantitySamplesOfType:(HKQuantityType *)quantityType
[self.healthStore executeQuery:query];
}

- (void)fetchSamplesOfType:(HKSampleType *)type
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(), ^{
if (type == [HKObjectType workoutType]) {
for (HKWorkout *sample in results) {
double energy = [[sample totalEnergyBurned] doubleValueForUnit:[HKUnit kilocalorieUnit]];
double distance = [[sample totalDistance] doubleValueForUnit:[HKUnit mileUnit]];
NSString *type = [RCTAppleHealthKit stringForHKWorkoutActivityType:[sample workoutActivityType]];

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

bool isTracked = true;
if ([[sample metadata][HKMetadataKeyWasUserEntered] intValue] == 1) {
isTracked = false;
}

NSString* device = @"";
if (@available(iOS 11.0, *)) {
device = [[sample sourceRevision] productType];
} else {
device = [[sample device] name];
if (!device) {
device = @"iPhone";
}
}

NSDictionary *elem = @{
@"activityId" : [NSNumber numberWithInt:[sample workoutActivityType]],
@"activityName" : type,
@"calories" : @(energy),
@"tracked" : @(isTracked),
@"sourceName" : [[[sample sourceRevision] source] name],
@"sourceId" : [[[sample sourceRevision] source] bundleIdentifier],
@"device": device,
@"distance" : @(distance),
@"start" : startDateString,
@"end" : endDateString
};

[data addObject:elem];
}
} else {
for (HKQuantitySample *sample in results) {
HKQuantity *quantity = sample.quantity;
double value = [quantity doubleValueForUnit:unit];

NSString * valueType = @"quantity";
if (unit == [HKUnit mileUnit]) {
valueType = @"distance";
}

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

bool isTracked = true;
if ([[sample metadata][HKMetadataKeyWasUserEntered] intValue] == 1) {
isTracked = false;
}

NSString* device = @"";
if (@available(iOS 11.0, *)) {
device = [[sample sourceRevision] productType];
} else {
device = [[sample device] name];
if (!device) {
device = @"iPhone";
}
}

NSDictionary *elem = @{
valueType : @(value),
@"tracked" : @(isTracked),
@"sourceName" : [[[sample sourceRevision] source] name],
@"sourceId" : [[[sample sourceRevision] source] bundleIdentifier],
@"device": device,
@"start" : startDateString,
@"end" : endDateString
};

[data addObject:elem];
}
}

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

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

[self.healthStore executeQuery:query];
}







- (void)setObserverForType:(HKSampleType *)type
unit:(HKUnit *)unit {
HKObserverQuery *query = [[HKObserverQuery alloc] initWithSampleType:type predicate:nil updateHandler:^(HKObserverQuery *query, HKObserverQueryCompletionHandler completionHandler, NSError * _Nullable error){
if (error) {
NSLog(@"*** An error occured while setting up the stepCount observer. %@ ***", error.localizedDescription);
return;
}
[self.bridge.eventDispatcher sendAppEventWithName:@"observer" body:@""];

// Theoretically, HealthKit expect that copletionHandler would be called at the end of query process,
// but it's unclear how to do in in event paradigm

// dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5);
// dispatch_after(delay, dispatch_get_main_queue(), ^(void){
// completionHandler();
// });
}];

[self.healthStore executeQuery:query];
[self.healthStore enableBackgroundDeliveryForType:type frequency:HKUpdateFrequencyImmediate withCompletion:^(BOOL success, NSError * _Nullable error) {
NSLog(@"success %s print some error %@", success ? "true" : "false", [error localizedDescription]);
}];
}

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


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

Expand Down
2 changes: 2 additions & 0 deletions RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

@interface RCTAppleHealthKit (TypesAndPermissions)

- (NSDictionary *)readPermsDict;
- (NSDictionary *)writePermsDict;
- (NSSet *)getReadPermsFromOptions:(NSArray *)options;
- (NSSet *)getWritePermsFromOptions:(NSArray *)options;

Expand Down
6 changes: 4 additions & 2 deletions RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// RCTAppleHealthKit
//
// Created by Greg Wilson on 2016-06-26.
// Copyright © 2016 Greg Wilson. All rights reserved.
// 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+TypesAndPermissions.h"
Expand Down Expand Up @@ -49,6 +50,8 @@ - (NSDictionary *)readPermsDict {
@"SleepAnalysis" : [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis],
// Mindfulness
@"MindfulSession" : [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierMindfulSession],
//workouts
@"Workout" : [HKObjectType workoutType],
};
return readPerms;
}
Expand Down Expand Up @@ -118,7 +121,6 @@ - (NSDictionary *)writePermsDict {
return writePerms;
}


// Returns HealthKit read permissions from options array
- (NSSet *)getReadPermsFromOptions:(NSArray *)options {
NSDictionary *readPermDict = [self readPermsDict];
Expand Down
3 changes: 2 additions & 1 deletion RCTAppleHealthKit/RCTAppleHealthKit+Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
+ (NSDate *)startDateFromOptions:(NSDictionary *)options;
+ (NSDate *)endDateFromOptions:(NSDictionary *)options;
+ (NSDate *)endDateFromOptionsDefaultNow:(NSDictionary *)options;
+ (HKUnit *)hkUnitFromOptions:(NSDictionary *)options;
+ (HKSampleType *)hkQuantityTypeFromString:(NSString *)type;

+ (HKUnit *)hkUnitFromOptions:(NSDictionary *)options key:(NSString *)key withDefault:(HKUnit *)defaultValue;
+ (NSUInteger)uintFromOptions:(NSDictionary *)options key:(NSString *)key withDefault:(NSUInteger)defaultValue;
Expand All @@ -33,5 +33,6 @@
+ (bool)boolFromOptions:(NSDictionary *)options key:(NSString *)key withDefault:(bool)defaultValue;

+ (NSMutableArray *)reverseNSMutableArray:(NSMutableArray *)array;
+ (NSString*) stringForHKWorkoutActivityType:(int) enumValue;

@end
Loading

0 comments on commit 715a0eb

Please sign in to comment.