Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unified way to get workouts + convert Activity Types to name + isTracked flag #25

Merged
merged 26 commits into from
Feb 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dda3ea9
isTracked step samples
EJohnF Jan 30, 2018
fd433d5
support multiple types of retrieving samples (just names confusing)
EJohnF Feb 3, 2018
2289471
add simple (only to console output) support for Workouts retrieving
EJohnF Feb 4, 2018
dd64358
extended support for workouts
EJohnF Feb 4, 2018
c3b5324
update work around isTracked flag, to perfom only one quert for track…
EJohnF Feb 4, 2018
d2a9ce0
add support for more information and fix isTracked flag
EJohnF Feb 4, 2018
34b3320
add ios10 and ios11 workout activities types
EJohnF Feb 6, 2018
e2aa87d
fix type naming
EJohnF Feb 6, 2018
f51978c
raw version of observers
EJohnF Feb 8, 2018
f4fb1a9
Slight updates for HealthKit observer
adamivancza Feb 9, 2018
482edad
fields renaming
EJohnF Feb 14, 2018
7108de7
change activityName from string to int
EJohnF Feb 14, 2018
c4a2d59
detect if quantity type is mile that write it as a distance field
EJohnF Feb 20, 2018
a28741e
change default units to metrics system
EJohnF Apr 23, 2018
836ee64
add more units
EJohnF Apr 24, 2018
9c5f273
remove depricated usage
EJohnF Apr 24, 2018
2178a8e
add function to check share(write) permissions authorisation
EJohnF May 11, 2018
e372559
fix background workouts observer
EJohnF May 18, 2018
8365a56
Fix for iOS 10
adamivancza Oct 2, 2018
169745b
Merge commit '950fe09616c6f8b6ae78b52eb0bc2253fdd44b20' into observer
EJohnF Nov 15, 2018
b186244
Merge commit '8365a5620ea08be9fde93b35f485a5c69b632136' into observer
EJohnF Nov 15, 2018
0b536cf
copy-right corrects. Decomposition. Fix error message. Fix differenc…
EJohnF Dec 23, 2018
531e1b3
update docs, change one of the returning key from getSamples
EJohnF Feb 5, 2019
3f91987
fix typo
EJohnF Feb 5, 2019
61a1e7c
change back default to imperial system, add activity name field, fix …
EJohnF Feb 26, 2019
181f4fa
remove checkPermission functions in order to use from PR #69
EJohnF Feb 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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