diff --git a/Constants/Permissions.js b/Constants/Permissions.js index a6e2d25b..4d2fdc27 100644 --- a/Constants/Permissions.js +++ b/Constants/Permissions.js @@ -66,5 +66,6 @@ export const Permissions = { SleepAnalysis: "SleepAnalysis", StepCount: "StepCount", Steps: "Steps", - Weight: "Weight" + Weight: "Weight", + Workout: "Workout" } diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.h b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.h index 235e9c6b..5bdecf42 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.h @@ -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) diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.m b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.m index 03b2724c..eeb0d91d 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Activity.m @@ -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" diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Body.m b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Body.m index ca11ae22..34fd3288 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Body.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Body.m @@ -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) { @@ -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], @@ -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 @@ -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]; diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h index 48108d6b..51449f86 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.h @@ -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; diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m index 1037bfdf..9f0e5e24 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Methods_Fitness.m @@ -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 { diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h index 8ca43a77..172152b6 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.h @@ -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 diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m index 6ca460cd..f18ceb36 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Queries.m @@ -10,8 +10,10 @@ #import "RCTAppleHealthKit+Queries.h" #import "RCTAppleHealthKit+Utils.h" -@implementation RCTAppleHealthKit (Queries) +#import +#import +@implementation RCTAppleHealthKit (Queries) - (void)fetchMostRecentQuantitySampleOfType:(HKQuantityType *)quantityType predicate:(NSPredicate *)predicate @@ -49,7 +51,6 @@ - (void)fetchMostRecentQuantitySampleOfType:(HKQuantityType *)quantityType [self.healthStore executeQuery:query]; } - - (void)fetchQuantitySamplesOfType:(HKQuantityType *)quantityType unit:(HKUnit *)unit predicate:(NSPredicate *)predicate @@ -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]; diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.h b/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.h index 5cb0891b..d69b7e95 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.h @@ -11,6 +11,8 @@ @interface RCTAppleHealthKit (TypesAndPermissions) +- (NSDictionary *)readPermsDict; +- (NSDictionary *)writePermsDict; - (NSSet *)getReadPermsFromOptions:(NSArray *)options; - (NSSet *)getWritePermsFromOptions:(NSArray *)options; diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m b/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m index 450c0338..6cb5a058 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+TypesAndPermissions.m @@ -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" @@ -49,6 +50,8 @@ - (NSDictionary *)readPermsDict { @"SleepAnalysis" : [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis], // Mindfulness @"MindfulSession" : [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierMindfulSession], + //workouts + @"Workout" : [HKObjectType workoutType], }; return readPerms; } @@ -118,7 +121,6 @@ - (NSDictionary *)writePermsDict { return writePerms; } - // Returns HealthKit read permissions from options array - (NSSet *)getReadPermsFromOptions:(NSArray *)options { NSDictionary *readPermDict = [self readPermsDict]; diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Utils.h b/RCTAppleHealthKit/RCTAppleHealthKit+Utils.h index 4501675e..7d00c782 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Utils.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Utils.h @@ -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; @@ -33,5 +33,6 @@ + (bool)boolFromOptions:(NSDictionary *)options key:(NSString *)key withDefault:(bool)defaultValue; + (NSMutableArray *)reverseNSMutableArray:(NSMutableArray *)array; ++ (NSString*) stringForHKWorkoutActivityType:(int) enumValue; @end diff --git a/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m b/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m index 7e0a834c..8a0c22b8 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit+Utils.m @@ -124,56 +124,18 @@ + (NSDate *)endDateFromOptionsDefaultNow:(NSDictionary *)options { return date; } -// ========== -// DEPRECATED -// ========== -+ (HKUnit *)hkUnitFromOptions:(NSDictionary *)options { - NSString *unitString = [options objectForKey:@"unit"]; - HKUnit *theUnit; - - if([unitString isEqualToString:@"gram"]){ - theUnit = [HKUnit gramUnit]; - } - if([unitString isEqualToString:@"pound"]){ - theUnit = [HKUnit poundUnit]; - } - if([unitString isEqualToString:@"meter"]){ - theUnit = [HKUnit meterUnit]; - } - if([unitString isEqualToString:@"mile"]){ - theUnit = [HKUnit mileUnit]; - } - if([unitString isEqualToString:@"inch"]){ - theUnit = [HKUnit inchUnit]; - } - if([unitString isEqualToString:@"foot"]){ - theUnit = [HKUnit footUnit]; - } - if([unitString isEqualToString:@"second"]){ - theUnit = [HKUnit secondUnit]; - } - if([unitString isEqualToString:@"minute"]){ - theUnit = [HKUnit minuteUnit]; - } - if([unitString isEqualToString:@"hour"]){ - theUnit = [HKUnit hourUnit]; - } - if([unitString isEqualToString:@"day"]){ - theUnit = [HKUnit dayUnit]; - } - if([unitString isEqualToString:@"joule"]){ - theUnit = [HKUnit jouleUnit]; - } - if([unitString isEqualToString:@"calorie"]){ - theUnit = [HKUnit calorieUnit]; - } - if([unitString isEqualToString:@"count"]){ - theUnit = [HKUnit countUnit]; - } - if([unitString isEqualToString:@"percent"]){ - theUnit = [HKUnit percentUnit]; - } - return theUnit; ++ (HKSampleType *)hkQuantityTypeFromString:(NSString *)type { + if ([type isEqual:@"Walking"]) { + return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; + } else if ([type isEqual:@"StairClimbing"]) { + return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierFlightsClimbed]; + } else if ([type isEqual:@"Running"]){ + return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; + } else if ([type isEqual:@"Cycling"]){ + return [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceCycling]; + } + // default [type isEqual:@"Workout"]) + return [HKObjectType workoutType]; } @@ -184,12 +146,21 @@ + (HKUnit *)hkUnitFromOptions:(NSDictionary *)options key:(NSString *)key withDe if([unitString isEqualToString:@"gram"]){ theUnit = [HKUnit gramUnit]; } + if([unitString isEqualToString:@"kg"]){ + theUnit = [HKUnit gramUnitWithMetricPrefix:HKMetricPrefixKilo]; + } + if([unitString isEqualToString:@"stone"]){ + theUnit = [HKUnit stoneUnit]; + } if([unitString isEqualToString:@"pound"]){ theUnit = [HKUnit poundUnit]; } if([unitString isEqualToString:@"meter"]){ theUnit = [HKUnit meterUnit]; } + if([unitString isEqualToString:@"cm"]){ + theUnit = [HKUnit meterUnitWithMetricPrefix:HKMetricPrefixCenti]; + } if([unitString isEqualToString:@"inch"]){ theUnit = [HKUnit inchUnit]; } @@ -321,4 +292,166 @@ + (NSMutableArray *)reverseNSMutableArray:(NSMutableArray *)array { return array; } ++ (NSString*)stringForHKWorkoutActivityType:(int) enumValue{ + switch( enumValue ){ + case HKWorkoutActivityTypeAmericanFootball: + return @"AmericanFootball"; + case HKWorkoutActivityTypeArchery: + return @"Archery"; + case HKWorkoutActivityTypeAustralianFootball: + return @"AustralianFootball"; + case HKWorkoutActivityTypeBadminton: + return @"Badminton"; + case HKWorkoutActivityTypeBaseball: + return @"Baseball"; + case HKWorkoutActivityTypeBasketball: + return @"Basketball"; + case HKWorkoutActivityTypeBowling: + return @"Bowling"; + case HKWorkoutActivityTypeBoxing: + return @"Boxing"; + case HKWorkoutActivityTypeClimbing: + return @"Climbing"; + case HKWorkoutActivityTypeCricket: + return @"Cricket"; + case HKWorkoutActivityTypeCrossTraining: + return @"CrossTraining"; + case HKWorkoutActivityTypeCurling: + return @"Curling"; + case HKWorkoutActivityTypeCycling: + return @"Cycling"; + case HKWorkoutActivityTypeDance: + return @"Dance"; + case HKWorkoutActivityTypeDanceInspiredTraining: + return @"DanceInspiredTraining"; + case HKWorkoutActivityTypeElliptical: + return @"Elliptical"; + case HKWorkoutActivityTypeEquestrianSports: + return @"EquestrianSports"; + case HKWorkoutActivityTypeFencing: + return @"Fencing"; + case HKWorkoutActivityTypeFishing: + return @"Fishing"; + case HKWorkoutActivityTypeFunctionalStrengthTraining: + return @"FunctionalStrengthTraining"; + case HKWorkoutActivityTypeGolf: + return @"Golf"; + case HKWorkoutActivityTypeGymnastics: + return @"Gymnastics"; + case HKWorkoutActivityTypeHandball: + return @"Handball"; + case HKWorkoutActivityTypeHiking: + return @"Hiking"; + case HKWorkoutActivityTypeHockey: + return @"Hockey"; + case HKWorkoutActivityTypeHunting: + return @"Hunting"; + case HKWorkoutActivityTypeLacrosse: + return @"Lacrosse"; + case HKWorkoutActivityTypeMartialArts: + return @"MartialArts"; + case HKWorkoutActivityTypeMindAndBody: + return @"MindAndBody"; + case HKWorkoutActivityTypeMixedMetabolicCardioTraining: + return @"MixedMetabolicCardioTraining"; + case HKWorkoutActivityTypePaddleSports: + return @"PaddleSports"; + case HKWorkoutActivityTypePlay: + return @"Play"; + case HKWorkoutActivityTypePreparationAndRecovery: + return @"PreparationAndRecovery"; + case HKWorkoutActivityTypeRacquetball: + return @"Racquetball"; + case HKWorkoutActivityTypeRowing: + return @"Rowing"; + case HKWorkoutActivityTypeRugby: + return @"Rugby"; + case HKWorkoutActivityTypeRunning: + return @"Running"; + case HKWorkoutActivityTypeSailing: + return @"Sailing"; + case HKWorkoutActivityTypeSkatingSports: + return @"SkatingSports"; + case HKWorkoutActivityTypeSnowSports: + return @"SnowSports"; + case HKWorkoutActivityTypeSoccer: + return @"Soccer"; + case HKWorkoutActivityTypeSoftball: + return @"Softball"; + case HKWorkoutActivityTypeSquash: + return @"Squash"; + case HKWorkoutActivityTypeStairClimbing: + return @"StairClimbing"; + case HKWorkoutActivityTypeSurfingSports: + return @"SurfingSports"; + case HKWorkoutActivityTypeSwimming: + return @"Swimming"; + case HKWorkoutActivityTypeTableTennis: + return @"TableTennis"; + case HKWorkoutActivityTypeTennis: + return @"Tennis"; + case HKWorkoutActivityTypeTrackAndField: + return @"TrackAndField"; + case HKWorkoutActivityTypeTraditionalStrengthTraining: + return @"TraditionalStrengthTraining"; + case HKWorkoutActivityTypeVolleyball: + return @"Volleyball"; + case HKWorkoutActivityTypeWalking: + return @"Walking"; + case HKWorkoutActivityTypeWaterFitness: + return @"WaterFitness"; + case HKWorkoutActivityTypeWaterPolo: + return @"WaterPolo"; + case HKWorkoutActivityTypeWaterSports: + return @"WaterSports"; + case HKWorkoutActivityTypeWrestling: + return @"Wrestling"; + case HKWorkoutActivityTypeYoga: + return @"Yoga"; + case HKWorkoutActivityTypeOther: + return @"Other"; + case HKWorkoutActivityTypeBarre: + return @"Barre"; + case HKWorkoutActivityTypeCoreTraining: + return @"CoreTraining"; + case HKWorkoutActivityTypeCrossCountrySkiing: + return @"CrossCountrySkiing"; + case HKWorkoutActivityTypeDownhillSkiing: + return @"DownhillSkiing"; + case HKWorkoutActivityTypeFlexibility: + return @"Flexibility"; + case HKWorkoutActivityTypeHighIntensityIntervalTraining: + return @"HighIntensityIntervalTraining"; + case HKWorkoutActivityTypeJumpRope: + return @"JumpRope"; + case HKWorkoutActivityTypeKickboxing: + return @"Kickboxing"; + case HKWorkoutActivityTypePilates: + return @"Pilates"; + case HKWorkoutActivityTypeSnowboarding: + return @"Snowboarding"; + case HKWorkoutActivityTypeStairs: + return @"Stairs"; + case HKWorkoutActivityTypeStepTraining: + return @"StepTraining"; + case HKWorkoutActivityTypeWheelchairWalkPace: + return @"WheelchairWalkPace"; + case HKWorkoutActivityTypeWheelchairRunPace: + return @"WheelchairRunPace"; + case HKWorkoutActivityTypeTaiChi: + return @"TaiChi"; + case HKWorkoutActivityTypeMixedCardio: + return @"MixedCardio"; + case HKWorkoutActivityTypeHandCycling: + return @"HandCycling"; + default:{ + NSException *e = [NSException + exceptionWithName:@"HKWorkoutActivityType InvalidValue" + reason:@"HKWorkoutActivityType can only have a value from the HKWorkoutActivityType enum" + userInfo:nil]; + @throw e; + } + } +} + @end diff --git a/RCTAppleHealthKit/RCTAppleHealthKit.h b/RCTAppleHealthKit/RCTAppleHealthKit.h index 27b8c036..6ccbe26b 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit.h +++ b/RCTAppleHealthKit/RCTAppleHealthKit.h @@ -16,9 +16,11 @@ @interface RCTAppleHealthKit : NSObject @property (nonatomic) HKHealthStore *healthStore; +@property BOOL isSync; - (void)isHealthKitAvailable:(RCTResponseSenderBlock)callback; - (void)initializeHealthKit:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; +- (void)checkPermission:(NSString *)input callback:(RCTResponseSenderBlock)callback; - (void)getModuleInfo:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback; @end diff --git a/RCTAppleHealthKit/RCTAppleHealthKit.m b/RCTAppleHealthKit/RCTAppleHealthKit.m index ccc4c26d..c91c4713 100644 --- a/RCTAppleHealthKit/RCTAppleHealthKit.m +++ b/RCTAppleHealthKit/RCTAppleHealthKit.m @@ -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.h" @@ -23,6 +24,7 @@ #import @implementation RCTAppleHealthKit + @synthesize bridge = _bridge; RCT_EXPORT_MODULE(); @@ -107,6 +109,17 @@ @implementation RCTAppleHealthKit [self fitness_getStepCountOnDay:input callback:callback]; } +RCT_EXPORT_METHOD(getSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) +{ + [self fitness_getSamples:input callback:callback]; +} + +RCT_EXPORT_METHOD(setObserver:(NSDictionary *)input) +{ + [self fitness_setObserver:input]; +} + + RCT_EXPORT_METHOD(getDailyStepCountSamples:(NSDictionary *)input callback:(RCTResponseSenderBlock)callback) { [self fitness_getDailyStepSamples:input callback:callback]; diff --git a/README.md b/README.md index 32663177..7940f8a1 100644 --- a/README.md +++ b/README.md @@ -102,37 +102,39 @@ AppleHealthKit.initHealthKit(options: Object, (err: string, results: Object) => * [initHealthKit](/docs/initHealthKit().md) * Realtime Methods * [initStepCountObserver](/docs/initStepCountObserver().md) + * [setObserver](/docs/setObserver().md) * Read Methods * [getActiveEnergyBurned](/docs/getActiveEnergyBurned().md) * [getBasalEnergyBurned](/docs/getBasalEnergyBurned().md) * [getBiologicalSex](/docs/getBiologicalSex().md) - * [getBloodGlucoseSamples](/docs/getbloodglucosesamples().md) - * [getBloodPressureSamples](/docs/getbloodpressuresamples().md) - * [getBodyTemperatureSamples](/docs/getbodytemperaturesamples().md) - * [getDailyDistanceCyclingSamples]() + * [getBloodGlucoseSamples](/docs/getBloodglucoseSamples().md) + * [getBloodPressureSamples](/docs/getBloodPressureSamples().md) + * [getBodyTemperatureSamples](/docs/getBodyTemperatureSamples().md) + * [getDailyDistanceCyclingSamples](/docs/getDailyDistanceCyclingSamples().md) * [getDailyDistanceWalkingRunningSamples](/docs/getDailyDistanceWalkingRunningSamples().md) * [getDailyFlightsClimbedSamples](/docs/getDailyFlightsClimbedSamples().md) * [getDailyStepCountSamples](/docs/getDailyStepCountSamples().md) * [getDateOfBirth](/docs/getDateOfBirth().md) - * [getDistanceCycling](/docs/getdistancecycling().md) + * [getDistanceCycling](/docs/getDistanceCycling().md) * [getDistanceWalkingRunning](/docs/getDistanceWalkingRunning().md) - * [getFlightsClimbed](/docs/getflightsclimbed().md) - * [getHeartRateSamples](/docs/getheartratesamples().md) - * [getHeightSamples](/docs/getheightsamples().md) - * [getLatestBmi](/docs/getlatestbmi().md) - * [getLatestBodyFatPercentage](/docs/getlatestbodyfatpercentage().md) - * [getLatestHeight](/docs/getlatestheight().md) - * [getLatestLeanBodyMass](/docs/getlatestleanbodymass().md) - * [getLatestWeight](/docs/getlatestweight().md) - * [getRespiratoryRateSamples](/docs/getrespiratoryratesamples().md) - * [getSleepSamples](/docs/getsleepsamples().md) + * [getFlightsClimbed](/docs/getFlightsClimbed().md) + * [getHeartRateSamples](/docs/getHeartRateSamples().md) + * [getHeightSamples](/docs/getHeightSamples().md) + * [getLatestBmi](/docs/getLatestBmi().md) + * [getLatestBodyFatPercentage](/docs/getLatestBodyFatPercentage().md) + * [getLatestHeight](/docs/getLatestHeight().md) + * [getLatestLeanBodyMass](/docs/getLatestLeanBodyMass().md) + * [getLatestWeight](/docs/getLatestWeight().md) + * [getRespiratoryRateSamples](/docs/getRespiratoryRateSamples().md) + * [getSleepSamples](/docs/getSleepSamples().md) * [getStepCount](/docs/getStepCount().md) - * [getWeightSamples](/docs/getweightsamples().md) + * [getWeightSamples](/docs/getWeightSamples().md) + * [getSamples](docs/getSamples().md) * Write Methods - * [saveBmi](/docs/savebmi().md) - * [saveHeight](/docs/saveheight().md) + * [saveBmi](/docs/saveBmi().md) + * [saveHeight](/docs/saveHeight().md) * [saveMindfulSession](/docs/saveMindfulSession().md) - * [saveWeight](/docs/saveweight().md) + * [saveWeight](/docs/saveWeight().md) * [saveSteps](/docs/saveSteps().md) * [References](#references) diff --git a/docs/getSamples().md b/docs/getSamples().md new file mode 100644 index 00000000..a4c6cc68 --- /dev/null +++ b/docs/getSamples().md @@ -0,0 +1,52 @@ +Query to get all activities of given type with extended information about it. + +```javascript 1.7 +let options = { + startDate: (new Date(2016,4,27)).toISOString(), + endDate: (new Date()).toISOString(), + type: 'Walking', // one of: ['Walking', 'StairClimbing', 'Running', 'Cycling', 'Workout'] +}; +``` + +The callback function will be called with a `samples` array containing objects with *value*, *startDate*, and *endDate* fields + +```javascript 1.7 +AppleHealthKit.getSamples(options, (err: Object, results: Array) => { + if (err) { + return; + } + console.log(results) +}); +``` + +Resulting object has different fields for different types. +In case of workout: +``` +{ + activityId: Number, // [NSNumber numberWithInt:[sample workoutActivityType]] + activityName: Number, // [RCTAppleHealthKit stringForHKWorkoutActivityType:[sample workoutActivityType]] + calories: Number, // [[sample totalEnergyBurned] doubleValueForUnit:[HKUnit kilocalorieUnit]] + tracked: Boolean, // [[sample metadata][HKMetadataKeyWasUserEntered] intValue] !== 1 + sourceName: String, // [[[sample sourceRevision] source] name] + sourceId: String, // [[[sample sourceRevision] source] bundleIdentifier] + device: String, // [[sample sourceRevision] productType] or 'iPhone' + distance: Number, // [[sample totalDistance] doubleValueForUnit:[HKUnit mileUnit]] + start: String, // [RCTAppleHealthKit buildISO8601StringFromDate:sample.startDate]; + end: String, // [RCTAppleHealthKit buildISO8601StringFromDate:sample.endDate]; +} +``` +for other types: +``` +{ + tracked: Boolean, // [[sample metadata][HKMetadataKeyWasUserEntered] intValue] !== 1 + sourceName: String, // [[[sample sourceRevision] source] name] + sourceId: String, // [[[sample sourceRevision] source] bundleIdentifier] + device: String, // [[sample sourceRevision] productType] or 'iPhone' + start: String, // [RCTAppleHealthKit buildISO8601StringFromDate:sample.startDate]; + end: String, // [RCTAppleHealthKit buildISO8601StringFromDate:sample.endDate]; + + //based on required type, one of the following will be present. + distance: Number, // [[sample totalDistance] doubleValueForUnit:[HKUnit mileUnit]] + calories: Number, // [[sample totalEnergyBurned] doubleValueForUnit:[HKUnit kilocalorieUnit]] +} +``` diff --git a/docs/setObserver().md b/docs/setObserver().md new file mode 100644 index 00000000..8f551613 --- /dev/null +++ b/docs/setObserver().md @@ -0,0 +1,15 @@ +Will listen for any updates in a given type data in healthKit and call app. + +type - one of the `['Walking', 'StairClimbing', 'Running', 'Cycling', 'Workout']` +```javascript 1.8 +import { NativeAppEventEmitter } from 'react-native'; +//...// +AppleHealthKit.setObserver({ type: 'Walking' }); +NativeAppEventEmitter.addListener( + 'observer', + callback + ); +``` + +So, callback would be call when new data of given type appears. When it happens, in order to get new info +need to call getSamples() function with proper arguments.