Getting Started with Apple's CareKit Framework

Apple's CareKit is an open source framework that gives developers the ability to create apps that help users monitor and manage medical conditions and their overall health, and then share that data with healthcare professionals. The goal is to put more power in the hands of end users and more information in the hands of the professionals charged with taking care of them. In this article, we run you through the various CareKit modules and show you how the different pieces fit together, using sample Swift code.

CareKit--which is part of Apple's HealthKit, along with the research-study-oriented ResearchKit--is now available on this GitHub page. There are several apps already taking advantage  of CareKit, including Glow Nurture, which lets users easily share information about their pregnancies with doctors, nurses or family members. In typical Apple fashion, CareKit ensures that user information privacy is strictly maintained and that users can see clearly what data is being shared.

Getting Started with CareKit

Including the CareKit framework in Your Project

To get started with an existing project, pull the latest stable version of CareKit from GitHub, as follows:

git clone -b stable --recurse-submodules https://github.com/carekit-apple/carekit.git

Opening up CareKit results in a fully baked CareKit .framework file, or you can simply drag CareKit.xcodeproj from the workspace into your own project.

Next, embed the framework as a dynamic framework in your Build Setting > General section, under Embedded Binaries as well as Linked Frameworks and Libraries.

Adding the ResearchKit framework toEmbedded Binaries

(source: Apple)

Care Plan Database

Before starting with the individual CareKit modules, it's important to understand the peristent database store, where data is stored for display. CareKit uses a OCKCarePlanStore for database storage, which it automatically loads and stores as needed. Developers don’t have access to the database directly, but rather use stored objects to interact with the database. Object Methods include storing or deleting activities; setting activity end dates; reading activities or events; and updating events.

We'll start with the Care Card feature for this review. Before working with Care Card, instatiate the care plan store, as follows:

let fileManager = NSFileManager.defaultManager()

guard let documentDirectory =   fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last else {
    fatalError("*** Error: Unable to get the document directory! ***")
 }

let storeURL = documentDirectory.URLByAppendingPathComponent("MyCareKitStore")
if !fileManager.fileExistsAtPath(storeURL.path!) {
    try! fileManager.createDirectoryAtURL(storeURL,     
    withIntermediateDirectories: true, attributes: nil)
}

In the code above, the goal is to instantiate a OCKCarePlanStore by generating a URL to your directory and verifying that the directory exists; otherwise, you create it.

Care Card

The Care Card module manages the tasks a user needs to perform as part of medical treatment. These tasks can include anything from taking medication to walking to changing patches. After you have instantiated the App Care Plan Store, as noted above, add desired intervention activities to the store. (Here, we used taking medication as an example.):

store.activityForIdentifier(MyMedicationIdentifier) { (success, activityOrNil, errorOrNil) -> Void in
     guard success else {
         // perform real error handling here.
         fatalError("*** An error occurred \(errorOrNil?.localizedDescription) ***")
     }

     if let activity = activityOrNil {

         // the activity already exists.

     } else {
         let medication = OCKCarePlanActivity(
             identifier: MyMedicationIdentifier,
             groupIdentifier: nil,
             type: .Intervention,
             title: "Vicodin",
             text: "5mg/500mg",
             tintColor: nil,
             instructions: "Take twice daily with food. May cause drowsiness. It is not recommended to drive with this medication. For any severe side effects, please contact your physician.",
             imageURL: nil,
             schedule: twiceADay,
             resultResettable: true,
             userInfo: nil)

         store.addActivity(medication, completion: { (bool, error) in
            // your completion block  
         })

     }
 }

In the code above, check if the activity already exists, using a unique identifier set for each activity (similar to UITableViewCell). Then, create the specific medication object before adding the activity with the medication. You can schedule activities as follows:

// take medication twice a day, every day starting March 15, 2016
 let startDay = NSDateComponents(year: 2016, month: 3, day: 15)
 let twiceADay = OCKCareSchedule.dailyScheduleWithStartDate(startDay, occurrencesPerDay: 2)

Now that we have added an intervention activity, we can go ahead and instantiate and present the care card view controller to the user:

let careCardViewController = OCKCareCardViewController(carePlanStore: store)
self.navigationController?.pushViewController(careCardViewController, animated: true)

The result would resemble something like this:

Care Card

(Source: Apple)

Symptom and Measurement Tracker

The Symptom and Measurement Tracker is used for tasks that involve subjective symptoms assessments, such as pain scales, and objective measurements, such as blood pressure levels, to determine the effectiveness of a patient’s care plan.

As with the Care Card, you need to first initiate the Care Plan Store, then add desired assessment activities, along with setting the symptom and measurement tracker delegate (OCKSymptomTrackerViewControllerDelegate):

store.activityForIdentifier(MyEmotionalSurveyIdentifier) { (success, activityOrNil, errorOrNil) -> Void in
     guard success else {
         // perform real error handling here.
         fatalError("*** An error occurred \(errorOrNil?.localizedDescription) ***")
     }

     if let activity = activityOrNil {

         // the activity already exists.

     } else {

        //activity object is immutable and thus cannot be changed later on
         let emotionalSurvey = OCKCarePlanActivity(
             identifier: MyEmotionalSurveyIdentifier,
             groupIdentifier: nil,
             type: .Assessment,
             title: "Daily Emotional Survey",
             text: "How are you feeling today?",
             tintColor: nil,
             instructions: nil,
             imageURL: nil,
             schedule: onceADay,
             resultResettable: false,
             userInfo: nil)

     }
 }

Next, instantiate and present the Symptom Tracker and Measurement View Controller:

let symptomTrackerController = OCKSymptomTrackerViewController(carePlanStore: store, delegate: self)
// presenting the view controller modally
presentViewController(symptomTrackerController, animated: true, completion: nil)
Symptom and Measurement Tracker

(Source: Apple)

Presenting Insights

Having set up the Care Card and Symptom Tracker, you can now present insights to the user in the form of charts and messages. This provides a rich visual articulation of how the treatment plan is going. Interestingly, you can present not just CareKit data but also arbitrary data, if it will help articulate a symptom and condition better.

You display one or more OCKInsightItem subclasses in an Insight scene, within the OCKInsightsViewController. The two concrete subclasses CareKit provide include OCKMessageItem and OCKBarChart.

You create and display simple messages using the OCKMessageItem subclass, allowing you to customize specific attributes of the object, as follows:

// Calculate the percentage of completed events.
let medicationAdherence = Float(completedEventCount) / Float(totalEventCount)

// Create an `OCKMessageItem` describing medical adherence.
let percentageFormatter = NSNumberFormatter()
percentageFormatter.numberStyle = .PercentStyle
let formattedAdherence = percentageFormatter.stringFromNumber(medicationAdherence)!

let message = OCKMessageItem(
    title: "Medication Adherence",
    text: "Your medication adherence was \(formattedAdherence) last week.",
    tintColor: Colors.Pink.color,
    messageType: .Tip)

With charts, you have the option of displaying one or more series of values. To do that, follow the steps Apple prescribes, in order:

  • Gather data that you wish displayed, (refer to Accessing Care Plan Data)
  • Set the labels you want for your chart (title, axis labels, axis sub-labels, etc.)
  • Scale or format your data in a meaningful way
  • Create one or more bar chart series
  • Create the bar chart itself

Creating the OCKBarSeries chart, you would start with something like:

    // Wait until all the data is gathered, then process the results.
dispatch_group_notify(gatherDataGroup, mainQueue) {

    // Generate the labels and data
    let dateStrings = completionData.map({(entry) -> String in

        guard let date = calendar.dateFromComponents(entry.dateComponent) else {
            fatalError("Unable to create date")
        }

        return NSDateFormatter.localizedStringFromDate(date, dateStyle: .ShortStyle, timeStyle: .NoStyle)
    })

    let completionValues = completionData.map({ (entry) -> Double in
        return entry.value
    })

    let completionValueLabels = completionValues.map({ (value) -> String in
        return NSNumberFormatter.localizedStringFromNumber(value, numberStyle: .PercentStyle)
    })

    let rawStressValues = completionData.map({ (entry) -> Double? in
        return stressAssessmentData[entry.dateComponent]
    })

    let stressValues = rawStressValues.map({ (valueOrNil) -> Double in
        guard let value = valueOrNil else {
            return 0.0
        }

        // Scale to match the completion values
        return value / 10.0
    })

    let stressValueLabels = rawStressValues.map({ (valueOrNil) -> String in
        guard let value = valueOrNil else {
            return ""
        }

        return NSNumberFormatter.localizedStringFromNumber(value, numberStyle: .DecimalStyle)
    })

    // Create the series

    let completionSeries = OCKBarSeries(
        title: "Treatment Plan Completed",
        values: completionValues,
        valueLabels: completionValueLabels,
        tintColor: UIColor.blueColor())

    let stressAssessmentSeries = OCKBarSeries(
        title: "Stress Assessment",
        values: stressValues,
        valueLabels: stressValueLabels,
        tintColor: UIColor.redColor())

    // Create the chart

    let chart = OCKBarChart(
        title: "Treatment Plan",
        text: "Compliance and Stress",
        tintColor: UIColor.greenColor(),
        axisTitles: dateStrings,
        axisSubtitles: nil,
        dataSeries: [completionSeries, stressAssessmentSeries])

    // Create the message

    let averageCompliance = completionValues.reduce(0.0, combine: +) / Double(completionValues.count)

    let messageString = "Over the last week, you completed an average of \(NSNumberFormatter.localizedStringFromNumber(averageCompliance, numberStyle: .PercentStyle)) of your treatment plan per day."

    let message = OCKMessageItem(
        title: "Compliance Rate",
        text: messageString,
        tintColor: UIColor.blackColor(),
        messageType: .Alert)

    // Update the view controller.
    insightViewControler.items = [chart, message]
}

We created the insights scene, within a OCKInsightsViewController

let insightsViewController = OCKInsightsViewController(
    insightItems: [message, chart],
    headerTitle: "Weekly Charts",
    headerSubtitle: nil)
Insights scene

(Source: Apple)

You can also generate documents (reports), in the form of HTML and PDF, which would encompass the charts and series. Refer to the API Documentation for information on that.

Connect

Connect enables patients to communicate their health status and insights to their care teams, as well as to friends and families, while ensuring their privacy is maintained.

In order to connect, you create contacts, through the OCKContact class, presenting specific information such as the contact name, phone number, email address and photo/avatar. You can create a new contact, as follows:

let newContact = OCKContact(contactType: .CareTeam,
    name: "Bill James",
    relation: "Nurse",
    tintColor: Colors.Green.color,
    phoneNumber: CNPhoneNumber(stringValue: "888-555-5512"),
    messageNumber: CNPhoneNumber(stringValue: "888-555-5512"),
    emailAddress: "billjames@example.com",
    monogram: "BJ",
    image: nil)

To present the list of contacts registered (connected), you use OCKConnectViewController:

let viewController = OCKConnectViewController(contacts: sampleData.contacts)
viewController.delegate = self

// Setup the controller's title and tab bar item
viewController.title = NSLocalizedString("Connect", comment: "")
viewController.tabBarItem = UITabBarItem(title: viewController.title, image: UIImage(named:"connect"), selectedImage: UIImage(named: "connect-filled"))

You also need to implement the mandatory delegate, OCKConnectViewControllerDelegate, as follows:

func connectViewController(connectViewController: OCKConnectViewController, didSelectShareButtonForContact contact: OCKContact)

The resulting view controller would resemble the following:

Contacts View

Contacts View

The user can edit and modify individual contacts:

Contacts View

Contacts View

For the complete source code, download Sample CareKit iOS App.

Conclusion

In this article, we presented Apple’s newest open source framework, CareKit, and showed you the benefits it brings it brings to care teams and physicians, as well as to patients, in monitoring and sharing vital medical conditions, symptoms and medications.

We showcased the various modules that make up CareKit, along with sample code snippets to help illustrate how you implement each of the features of the framework.

This is indeed early days as far as the ResearchKit and HealthKit frameworks are concerned, and we will no doubt see the community engage and propose new features and functionality during the next few years.

Doron Katz A keen passion for emerging technologies, practices and methodologies, Doron embraces the vision of lean development with continuous customer development. Consultant for various startups, as a Project and Product Manager, with a mobile engineering background in iOS, and over 10 years of professional web development experience.

Comments