Prince Kumar Sharma
Prince Kumar Sharma

Reputation: 12641

How Health Records can be added to health App via Health Kit

I am able to read write Sample data in health App like body Mass, Height, Weight and read from profile data like Age, Gender,Blood Type etc. My Piece of code for request auth and read/write methods are given below.

- (void)requestAuthorization {

    if ([HKHealthStore isHealthDataAvailable] == NO) {
        // If our device doesn't support HealthKit -> return.
        return;
    }

    HKObjectType *dateOfBirth = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];
    HKObjectType *bloodType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBloodType];
    HKObjectType *biologicalSex = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
    HKObjectType *wheelChairUse = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierWheelchairUse];
    HKObjectType *skinType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierFitzpatrickSkinType];

    HKObjectType *bodyMassIndex = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMassIndex];
    HKObjectType *height = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    HKObjectType *bodyMass = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKObjectType *activeEnergy = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];

    HKObjectType *heartRate = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
    HKObjectType *bloodGlucose = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];
    HKObjectType *bodyTemprature = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];
    HKObjectType *respiratoryRate = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierRespiratoryRate];
    HKObjectType *oxygenSaturation = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierOxygenSaturation];
    HKObjectType *fatPercentage = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyFatPercentage];
    HKObjectType *waistCircumference = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierWaistCircumference];
    HKObjectType *cholestrol = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDietaryCholesterol];


    NSArray *healthKitTypesToWrite = @[bodyMassIndex,
                                       activeEnergy,
                                       HKObjectType.workoutType];

    NSArray *healthKitTypesToRead = @[dateOfBirth,
                                      bloodType,
                                      biologicalSex,
                                      bodyMassIndex,
                                      height,
                                      bodyMass,
                                      wheelChairUse,
                                      skinType,
                                      heartRate,
                                      bloodGlucose,
                                      bodyTemprature,
                                      respiratoryRate,
                                      oxygenSaturation,
                                      fatPercentage,
                                      waistCircumference,
                                      cholestrol,
                                      HKObjectType.workoutType];

    [self.healthStore requestAuthorizationToShareTypes:[NSSet setWithArray:healthKitTypesToWrite] readTypes:[NSSet setWithArray:healthKitTypesToRead] completion:^(BOOL success, NSError * _Nullable error) {

    }];
}

pragma mark- Helper

- (void )getMostRecentSampleForType:(HKSampleType *)sampleType
                  withResultHandler:(HKQueryResultHandler )handler {

    NSPredicate *mostRecentPredicate = [HKQuery predicateForSamplesWithStartDate:NSDate.distantPast endDate:[NSDate date] options:HKQueryOptionStrictEndDate];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierStartDate ascending:false];
    NSInteger limit = 1;

    HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:mostRecentPredicate limit:limit sortDescriptors:@[sortDescriptor] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *>* _Nullable results, NSError * _Nullable error) {

        dispatch_async(dispatch_get_main_queue(), ^{
            // do work here
            if (handler) {
                if (results && results.count > 0) {
                    HKQuantitySample *sample = (HKQuantitySample *)[results firstObject];
                    handler(sample,nil);
                }else {
                    handler(nil,error);
                }
            }
        });
    }];

    HKHealthStore *store = [[HKHealthStore alloc] init];
    [store executeQuery:sampleQuery];
}

pragma mark- Read methods

- (NSDate *)readBirthDate {
    NSError *error;

    NSDateComponents *components = [self.healthStore dateOfBirthComponentsWithError:&error];
    // Convenience method of HKHealthStore to get date of birth directly.

    NSCalendar *cal = [NSCalendar currentCalendar];
    [cal setTimeZone:[NSTimeZone localTimeZone]];
    [cal setLocale:[NSLocale currentLocale]];
    NSDate *dateOfBirth = [cal dateFromComponents:components];

    if (!dateOfBirth) {
        NSLog(@"Either an error occured fetching the user's age information or none has been stored yet. In your app, try to handle this gracefully.");
    }
    return dateOfBirth;
}

- (NSString *)biologicalSex {
    HKBiologicalSexObject *genderObj = [self.healthStore biologicalSexWithError:nil];
    HKBiologicalSex gender = genderObj.biologicalSex;

    switch (gender) {
        case HKBiologicalSexNotSet:
            return @"";
        case HKBiologicalSexFemale:
            return @"Female";
        case HKBiologicalSexMale:
            return @"Male";
        case HKBiologicalSexOther:
            return @"Other";
        default:
            break;
    }
    return @"";
}

- (NSString *)weight {
    HKSampleType *weightSampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    [self getMostRecentSampleForType:weightSampleType withResultHandler:^(HKQuantitySample *sample, NSError *error) {
        if (!errno) {
            HKQuantity *quantity = sample.quantity;
            HKUnit *kilogramUnit = [HKUnit gramUnitWithMetricPrefix:HKMetricPrefixKilo];
            double weight = [quantity doubleValueForUnit:kilogramUnit];
            NSLog(@"weight = %.0f Kg",weight);
        }
    }];
    return @"";
}

pragma mark- write method

- (void)writeWeightSample:(CGFloat)weight {

    // Each quantity consists of a value and a unit.
    HKUnit *kilogramUnit = [HKUnit gramUnitWithMetricPrefix:HKMetricPrefixKilo];
    HKQuantity *weightQuantity = [HKQuantity quantityWithUnit:kilogramUnit doubleValue:weight];

    HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    NSDate *now = [NSDate date];

    // For every sample, we need a sample type, quantity and a date.
    HKQuantitySample *weightSample = [HKQuantitySample quantitySampleWithType:weightType quantity:weightQuantity startDate:now endDate:now];

    [self.healthStore saveObject:weightSample withCompletion:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error while saving weight (%f) to Health Store: %@.", weight, error);
        }
    }];
}

But I am not finding any clue how to add records in Health Data->Health Records

Health Record

No Data

Upvotes: 5

Views: 1628

Answers (3)

Prince Kumar Sharma
Prince Kumar Sharma

Reputation: 12641

I am not familiar with HL7 and CDA Pro as I don't belong to Health Industry. So I cannot add codeSystem,code,codeSystemName etc. with proper entries. Although I found a way to generate own CDA file with customisation just to display in health records.

You need to keep placeholder CDA file in resource bundle. visitSummary.xml

- (void)addToHealth {      

    NSURL *cdaURL = [[NSBundle mainBundle] URLForResource:@"visitSummary" withExtension:@"xml"];
    NSError *parsingError = nil;
    NSString *string = [NSString stringWithContentsOfURL:cdaURL encoding:NSUTF8StringEncoding error:&parsingError];

    string = [string stringByReplacingOccurrencesOfString:@"DOCUMENT_ID" withString:@"DOC449"];
    string = [string stringByReplacingOccurrencesOfString:@"PAT_NAME" withString:@"Alan Joseph"];
    string = [string stringByReplacingOccurrencesOfString:@"MOBILE_NO" withString:@"9643453174"];
    string = [string stringByReplacingOccurrencesOfString:@"EMAIL_ID" withString:@"[email protected]"];
    string = [string stringByReplacingOccurrencesOfString:@"PATIENT_ID" withString:@"PID242000012"];
    string = [string stringByReplacingOccurrencesOfString:@"DATE_OF_BIRTH" withString:@"19920515"];

    string = [string stringByReplacingOccurrencesOfString:@"PAT_STREET_LINE" withString:@"Prithviraj Market, Khan Market"];
    string = [string stringByReplacingOccurrencesOfString:@"PAT_CITY" withString:@"New Delhi"];
    string = [string stringByReplacingOccurrencesOfString:@"PAT_STATE" withString:@"Delhi"];
    string = [string stringByReplacingOccurrencesOfString:@"PAT_POSTAL" withString:@"110003"];

    string = [string stringByReplacingOccurrencesOfString:@"GENDER_CODE" withString:@"M"];
    string = [string stringByReplacingOccurrencesOfString:@"GENDER_DISPLAY" withString:@"Male"];

    string = [string stringByReplacingOccurrencesOfString:@"MODEL_NAME" withString:@"iPad Air"];
    string = [string stringByReplacingOccurrencesOfString:@"AUTHOR_NAME" withString:@"Mark"];
    string = [string stringByReplacingOccurrencesOfString:@"CUSTODIAN_NAME" withString:@"Dr. Lauren"];

    string = [string stringByReplacingOccurrencesOfString:@"ALLERGY_COMPONENTS" withString:[self allergyComponents]];
    string = [string stringByReplacingOccurrencesOfString:@"FAMILY_HISTORY_COMPONENTS" withString:[self familyHistoryComponents]];
        string = [string stringByReplacingOccurrencesOfString:@"VITALS_COMPONENTS" withString:[self vitalSignsComponents]];

    NSData *cdaData = [string dataUsingEncoding:NSUTF8StringEncoding];

    NSError *validationError = nil;
    NSDate *date = [NSDate date];
    HKSample *cdaSample = [HKCDADocumentSample CDADocumentSampleWithData:cdaData startDate:date endDate:date metadata:nil validationError:&validationError];

    if (cdaSample != nil) {
        [self.healthStore saveObject:cdaSample withCompletion:^(BOOL success, NSError * _Nullable error) {
            if (!success) {
                NSLog(@"Error while saving CDA to Health Store: %@.", error);
            }
        }];
    }else {
        NSLog(@"No records");
    }
}

Although you need to add one more HKObjectType request i.e; CDA file.

HKObjectType *cdaObjType = [HKObjectType documentTypeForIdentifier:HKDocumentTypeIdentifierCDA];

NSArray *healthKitTypesToWrite = @[bodyMassIndex,
                                   activeEnergy,
                                   cdaObjType,
                                   HKObjectType.workoutType];

NSArray *healthKitTypesToRead = @[dateOfBirth,
                                  bloodType,
                                  biologicalSex,
                                  bodyMassIndex,
                                  height,
                                  bodyMass,
                                  wheelChairUse,
                                  skinType,
                                  heartRate,
                                  bloodGlucose,
                                  bodyTemprature,
                                  respiratoryRate,
                                  oxygenSaturation,
                                  fatPercentage,
                                  waistCircumference,
                                  cholestrol,
                                  cdaObjType,
                                  HKObjectType.workoutType];

[self.healthStore requestAuthorizationToShareTypes:[NSSet setWithArray:healthKitTypesToWrite] readTypes:[NSSet setWithArray:healthKitTypesToRead] completion:^(BOOL success, NSError * _Nullable error) {

}];

Helper methods to prepare components.

- (NSString *)allergyComponents {

    NSMutableArray *allergyComponents = [[NSMutableArray alloc] init];

    [allergyComponents addObject:@"<component>"];
    [allergyComponents addObject:@"<section>"];
    [allergyComponents addObject:@"<title>Allergies</title>"];
    [allergyComponents addObject:@"<text>"];
    [allergyComponents addObject:@"<table border=\"1\" width=\"100%\">"];

    [allergyComponents addObject:@"<thead><tr>"];
    [allergyComponents addObject:@"<th>Name</th>"];
    [allergyComponents addObject:@"<th>Type</th>"];
    [allergyComponents addObject:@"<th>Exists From</th>"];
    [allergyComponents addObject:@"</tr></thead>"];
    [allergyComponents addObject:@"<tbody>"];

    for (int i = 0; i<3; i++) {
        [allergyComponents addObject:@"<tr>"];
        [allergyComponents addObject:@"<td>Abalone,perlemoen</td>"];
        [allergyComponents addObject:@"<td>Food Allergy</td>"];
        [allergyComponents addObject:@"<td>29 Feb</td>"];
        [allergyComponents addObject:@"</tr>"];
    }

    [allergyComponents addObject:@"</tbody>"];
    [allergyComponents addObject:@"</table>"];
    [allergyComponents addObject:@"</text>"];
    [allergyComponents addObject:@"</section>"];
    [allergyComponents addObject:@"</component>"];

    return [allergyComponents componentsJoinedByString:@""];
}

- (NSString *)familyHistoryComponents {

    NSMutableArray *familyComponents = [[NSMutableArray alloc] init];

    [familyComponents addObject:@"<component>"];
    [familyComponents addObject:@"<section>"];
    [familyComponents addObject:@"<title>Family History</title>"];
    [familyComponents addObject:@"<text>"];
    [familyComponents addObject:@"<table border=\"1\" width=\"100%\">"];

    [familyComponents addObject:@"<thead><tr>"];
    [familyComponents addObject:@"<th>Relation</th>"];
    [familyComponents addObject:@"<th>Condition</th>"];
    [familyComponents addObject:@"</tr></thead>"];
    [familyComponents addObject:@"<tbody>"];

    for (int i = 0; i<2; i++) {
        [familyComponents addObject:@"<tr>"];
        [familyComponents addObject:@"<td>Father</td>"];
        [familyComponents addObject:@"<td>A, Hemophilia</td>"];
        [familyComponents addObject:@"</tr>"];
    }

    [familyComponents addObject:@"</tbody>"];
    [familyComponents addObject:@"</table>"];
    [familyComponents addObject:@"</text>"];
    [familyComponents addObject:@"</section>"];
    [familyComponents addObject:@"</component>"];

    return [familyComponents componentsJoinedByString:@""];
}

- (NSString *)vitalSignsComponents {

    NSMutableArray *vitalSigns = [[NSMutableArray alloc] init];

    [vitalSigns addObject:@"<component>"];
    [vitalSigns addObject:@"<section>"];
    [vitalSigns addObject:@"<title>Vital Signs</title>"];
    [vitalSigns addObject:@"<text>"];
    [vitalSigns addObject:@"<table border=\"1\" width=\"100%\">"];

    [vitalSigns addObject:@"<thead><tr>"];
    [vitalSigns addObject:@"<th align=\"right\">Date</th>"];
    [vitalSigns addObject:@"<th>Vital</th>"];
    [vitalSigns addObject:@"<th>Reading</th>"];
    [vitalSigns addObject:@"</tr></thead>"];
    [vitalSigns addObject:@"<tbody>"];

    for (int i = 0; i<2; i++) {
        [vitalSigns addObject:@"<tr>"];
        [vitalSigns addObject:@"<th align=\"left\">26-10-2017</th>"];
        [vitalSigns addObject:@"<td>Pulse</td>"];
        [vitalSigns addObject:@"<td>12 bpm</td>"];
        [vitalSigns addObject:@"</tr>"];
    }

    [vitalSigns addObject:@"</tbody>"];
    [vitalSigns addObject:@"</table>"];
    [vitalSigns addObject:@"</text>"];
    [vitalSigns addObject:@"</section>"];
    [vitalSigns addObject:@"</component>"];

    return [vitalSigns componentsJoinedByString:@""];
}

And here is output how it will look like in health App.

enter image description here

Preview will look like.

enter image description here

Upvotes: 1

Firozzz
Firozzz

Reputation: 88

// 1. Create a BMI Sample
let bmiType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMassIndex)
let bmiQuantity = HKQuantity(unit: HKUnit.count(), doubleValue: bmi)
let bmiSample = HKQuantitySample(type: bmiType!, quantity: bmiQuantity, start: date as Date, end: date as Date)

// 2. Save the sample in the store
healthKitStore.save(bmiSample, withCompletion: { (success, error) -> Void in
    if( error != nil ) {
        print("Error saving BMI sample: \(String(describing: error?.localizedDescription))")
    } else {
        print("BMI sample saved successfully!")
    }
})

Upvotes: 0

Ron Srebro
Ron Srebro

Reputation: 6862

Apple is using this section to show data retrieved with CDA files. CDA is a standard that allows interoperability between health providers. You are likely to be able to download your own CDA from your health provider patient portal.

There aren't many apps out there that will import CDA files to HealthKit, but apple has a code sample that will create a dummy CDA file and import it into HealthKit

Checkout the LoopHealth sample from app https://developer.apple.com/library/content/samplecode/LoopHealth/Introduction/Intro.html#//apple_ref/doc/uid/TP40017553-Intro-DontLinkElementID_2

Upvotes: 1

Related Questions