Sample Code

Accessing a User’s Clinical Records

Display a user’s clinical records by requesting authorization and querying HealthKit for the records.

Download

Overview

This sample demonstrates how to request access to a user’s clinical records through HealthKit. With HealthKit’s clinical record support, you can read Fast Healthcare Interoperability Resources (FHIR) objects from the HealthKit store. Users must first download their clinical records from one of the supported healthcare institutions.

Configure the Sample Code Project

To use HealthKit, you must first enable the HealthKit capability and include the NSHealthShareUsageDescription key in your app’s Info.plist file, as described in Accessing Health Records. To access the clinical records, you must check the Clinical Health Records checkbox in the HealthKit capability and include the NSHealthClinicalHealthRecordsShareUsageDescription key in your app’s Info.plist file.

In this sample app, the capability is already enabled and the usage string is provided.

Before building and running the app:

  1. Set a valid signing team in the target’s General pane so that Xcode can create a provisioning profile containing the HealthKit entitlement when you build the app for the first time.

  2. Add sample data to the Health app by connecting a valid patient portal account from a supported healthcare institution. If you don’t have such an account, you can add sample data within the Simulator as described in Accessing Sample Data in the Simulator.

Define the Sample Types to Request

Define the clinical record sample types using the HKClinicalTypeIdentifier enumeration. You must request permission to read all the types that your app intends to use. Note that you may define both clinical records and standard HealthKit sample types at the same time.

enum Section {
    case healthRecords
    case fitnessData

    var displayName: String {
        switch self {
        case .healthRecords:
            return "Health Records"
        case .fitnessData:
            return "Fitness Data"
        }
    }

    var types: [HKSampleType] {
        switch self {
        case .healthRecords:
            return [
                HKObjectType.clinicalType(forIdentifier: .allergyRecord)!,
                HKObjectType.clinicalType(forIdentifier: .vitalSignRecord)!,
                HKObjectType.clinicalType(forIdentifier: .conditionRecord)!,
                HKObjectType.clinicalType(forIdentifier: .immunizationRecord)!,
                HKObjectType.clinicalType(forIdentifier: .labResultRecord)!,
                HKObjectType.clinicalType(forIdentifier: .medicationRecord)!,
                HKObjectType.clinicalType(forIdentifier: .procedureRecord)!,
            ]

        case .fitnessData:
            return [
                HKObjectType.quantityType(forIdentifier: .stepCount)!,
                HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!,
            ]
        }
    }
}

Request Authorization

You may request authorization to access both clinical data and HealthKit data simultaneously.

let healthStore = HKHealthStore()

var sampleTypes: Set<HKSampleType> {
    return Set(Section.healthRecords.types + Section.fitnessData.types)
}

@objc
func requestAuthorizationIfNeeded(_ sender: AnyObject? = nil) {
    healthStore.getRequestStatusForAuthorization(toShare: Set(), read: sampleTypes) { (status, error) in
        if status == .shouldRequest {
            self.requestAuthorization(sender)
        } else {
            DispatchQueue.main.async {
                let message = "Authorization status has been determined, no need to request authorization at this time"
                self.present(message: message, titled: "Already Requested")
            }
        }
    }
}

@objc
func requestAuthorization(_ sender: AnyObject? = nil) {
    healthStore.requestAuthorization(toShare: nil, read: sampleTypes) { (success, error) in
        guard success else {
            DispatchQueue.main.async {
                self.handleError(error)
            }
            return
        }
    }
}

Query for Health Records

Use HKSampleQuery:

func queryForSamples() {
    let sortDescriptors = [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)]
    let query = HKSampleQuery(sampleType: sampleType, predicate: nil, limit: 100, sortDescriptors: sortDescriptors) {(_, samplesOrNil, error) in
        DispatchQueue.main.async {
            guard let samples = samplesOrNil else {
                self.handleError(error)
                return
            }
            
            self.samples = samples
            self.tableView.reloadData()
        }
    }
    
    healthStore.execute(query)
}

Access Elements Within a FHIR Resource

Once the user has given you access to their clinical records, you’ll need to pull out the relevant information from the FHIR JSON data to do something useful with it. For example, to display the status of a medication in the form of a MedicationStatement resource, you need to access the MedicationStatement.status element.

You can create a type conforming to the Codable protocol for easy parsing from JSON:

struct Coding: Codable {
    var code: String
    var display: String?
    var system: String
    var version: String?
}

struct CodeableConcept: Codable {
    var coding: [Coding]
    var text: String?
}

struct MedicationStatement: Codable {
    var medicationCodeableConcept: CodeableConcept
    var status: String
}

Use a JSONDecoder to deserialize the resource’s JSON data:

func displayItem(for sample: HKSample) -> DisplayItem {
    guard let clinicalRecord = sample as? HKClinicalRecord, let fhirResource = clinicalRecord.fhirResource else {
        return DisplayItem.errorItem
    }
    do {
        let decodedResource = try decode(resource: fhirResource)
        return DisplayItem(title: clinicalRecord.displayName, subtitle: decodedResource.displayItemSubtitle, accessory: .disclosure)
    } catch {
        return DisplayItem(title: clinicalRecord.displayName, subtitle: error.localizedDescription, accessory: .none)
    }
}

func decode(resource: HKFHIRResource) throws -> DisplayItemSubtitleConvertible {
    func decode<T: DisplayItemSubtitleConvertible & Codable>(as type: T.Type) throws -> DisplayItemSubtitleConvertible {
        let decoder = JSONDecoder()
        return try decoder.decode(type.self, from: resource.data)
    }
    
    switch resource.resourceType {
    case .allergyIntolerance:
        return try decode(as: AllergyIntolerance.self)
    case .condition:
        return try decode(as: Condition.self)
    case .immunization:
        return try decode(as: Immunization.self)
    case .medicationDispense:
        return try decode(as: MedicationDispense.self)
    case .medicationOrder:
        return try decode(as: MedicationOrder.self)
    case .medicationStatement:
        return try decode(as: MedicationStatement.self)
    case .observation:
        return try decode(as: Observation.self)
    case .procedure:
        return try decode(as: Procedure.self)
    default:
        throw FHIRResourceDecodingError.resourceTypeNotSupported(resource.resourceType)
    }
}

You now have direct access to the status element and can display it to the user. In the sample app, it appears as the subtitle of the Medication list view.

extension MedicationStatement: DisplayItemSubtitleConvertible {
    var displayItemSubtitle: String {
        return self.status
    }
}

See Also

Medical Records

Accessing Health Records

Read clinical record data from the HealthKit store.

Accessing Sample Data in the Simulator

Set up sample accounts to build and test your app.

class HKClinicalRecord

A sample that stores a clinical record.

class HKFHIRResource

An object containing Fast Healthcare Interoperability Resources (FHIR) data.

class HKCDADocumentSample

A Clinical Document Architecture (CDA) sample that stores a single document.

class HKDocumentSample

The HKDocumentSample class is an abstract class that represents a health document in the HealthKit store.

static let CDA: HKDocumentTypeIdentifier

The CDA Document type identifier, used when requesting permission to read or share CDA documents.

class HKDocumentType

The HKDocumentType class is a concrete subclass of the HKSampleType class, used when creating queries for documents.