EddyVerbruggen / HealthKit

Cordova plugin for the iOS HealthKit framework
MIT License
167 stars 215 forks source link

query correlation type should specify more than one unit type #81

Open ghost opened 7 years ago

ghost commented 7 years ago

correlations don't have always one unit of measurement. The example comes with food, where a correlation can have weights (like for sugar, or vitamins) as well as energy (calories) and volumes (litres of water). A solution could be, instead of passing one unit, to pass an array of units.

Here is how the implementation would look like:

- (void)queryCorrelationType:(CDVInvokedUrlCommand *)command {
    NSDictionary *args = command.arguments[0];
    NSDate *startDate = [NSDate dateWithTimeIntervalSince1970:[args[HKPluginKeyStartDate] longValue]];
    NSDate *endDate = [NSDate dateWithTimeIntervalSince1970:[args[HKPluginKeyEndDate] longValue]];
    NSString *correlationTypeString = args[HKPluginKeyCorrelationType];
    NSArray<NSString *> *unitsString = args[HKPluginKeyUnits];

    HKCorrelationType *type = (HKCorrelationType *) [HealthKit getHKSampleType:correlationTypeString];
    if (type == nil) {
        [HealthKit triggerErrorCallbackWithMessage:@"sampleType was invalid" command:command delegate:self.commandDelegate];
        return;
    }
    NSMutableArray *units = [[NSMutableArray alloc] init];
    for (NSString *unitString in unitsString) {
        HKUnit *unit = ((unitString != nil) ? [HKUnit unitFromString:unitString] : nil);
        [units addObject:unit];
    }

    // TODO check that unit is compatible with sampleType if sample type of HKQuantityType
    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];

    HKCorrelationQuery *query = [[HKCorrelationQuery alloc] initWithType:type predicate:predicate samplePredicates:nil completion:^(HKCorrelationQuery *correlationQuery, NSArray *correlations, NSError *error) {
        __block HealthKit *bSelf = self;
        if (error) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                [HealthKit triggerErrorCallbackWithMessage:error.localizedDescription command:command delegate:bSelf.commandDelegate];
            });
        } else {
            NSMutableArray *finalResults = [[NSMutableArray alloc] initWithCapacity:correlations.count];
            for (HKSample *sample in correlations) {
                NSDate *startSample = sample.startDate;
                NSDate *endSample = sample.endDate;

                NSMutableDictionary *entry = [NSMutableDictionary dictionary];
                entry[HKPluginKeyStartDate] = [HealthKit stringFromDate:startSample];
                entry[HKPluginKeyEndDate] = [HealthKit stringFromDate:endSample];

                // common indices
                entry[HKPluginKeyUUID] = sample.UUID.UUIDString;
                if (sample.metadata == nil || ![NSJSONSerialization isValidJSONObject:sample.metadata]) {
                    entry[HKPluginKeyMetadata] = @{};
                } else {
                    entry[HKPluginKeyMetadata] = sample.metadata;
                }

                if ([sample isKindOfClass:[HKCategorySample class]]) {

                    HKCategorySample *csample = (HKCategorySample *) sample;
                    entry[HKPluginKeyValue] = @(csample.value);
                    entry[@"categoryType.identifier"] = csample.categoryType.identifier;
                    entry[@"categoryType.description"] = csample.categoryType.description;

                } else if ([sample isKindOfClass:[HKCorrelation class]]) {

                    HKCorrelation *correlation = (HKCorrelation *) sample;
                    entry[HKPluginKeyCorrelationType] = correlation.correlationType.identifier;

                    NSMutableArray *samples = [NSMutableArray arrayWithCapacity:correlation.objects.count];
                    for (HKQuantitySample *quantitySample in correlation.objects) {
                        for (int i=0; i<[units count]; i++) {
                            HKUnit *unit = units[i];
                            NSString *unitS = unitsString[i];
                            if ([quantitySample.quantity isCompatibleWithUnit:unit]) {
                                [samples addObject:@{
                                                     HKPluginKeyStartDate: [HealthKit stringFromDate:quantitySample.startDate],
                                                     HKPluginKeyEndDate: [HealthKit stringFromDate:quantitySample.endDate],
                                                     HKPluginKeySampleType: quantitySample.sampleType.identifier,
                                                     HKPluginKeyValue: @([quantitySample.quantity doubleValueForUnit:unit]),
                                                     HKPluginKeyUnit: unitS,
                                                     HKPluginKeyMetadata: ((quantitySample.metadata != nil) ? quantitySample.metadata : @{}),
                                                     HKPluginKeyUUID: quantitySample.UUID.UUIDString
                                                     }
                                 ];
                                break;
                            }
                        }
                    }
                    entry[HKPluginKeyObjects] = samples;

                } else if ([sample isKindOfClass:[HKQuantitySample class]]) {

                    HKQuantitySample *qsample = (HKQuantitySample *) sample;
                    for (int i=0; i<[units count]; i++) {
                        HKUnit *unit = units[i];
                        if ([qsample.quantity isCompatibleWithUnit:unit]) {
                            double quantity = [qsample.quantity doubleValueForUnit:unit];
                            entry[@"quantity"] = [NSString stringWithFormat:@"%f", quantity];
                            break;
                        }
                    }

                } else if ([sample isKindOfClass:[HKWorkout class]]) {

                    HKWorkout *wsample = (HKWorkout *) sample;
                    entry[@"duration"] = @(wsample.duration);

                } else if ([sample isKindOfClass:[HKCorrelationType class]]) {
                    // TODO
                    // wat do?
                }

                [finalResults addObject:entry];
            }

            dispatch_sync(dispatch_get_main_queue(), ^{
                CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:finalResults];
                [bSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
            });
        }
    }];
    [[HealthKit sharedHealthStore] executeQuery:query];
}

you will also need to define HKPluginKeyUnits at the beginning as:

static NSString *const HKPluginKeyUnits = @"units";

I am using this code myself and it's working good!

EddyVerbruggen commented 7 years ago

Interesting change! Is this something you can easily submit as a PR?

ghost commented 7 years ago

done. Let me know.