Apple Health Frameworks

Nov 29 2022 · Swift 5, iOS 15, Xcode 13

Part 3: Take Advantage of CareKit & ResearchKit

12. Extract OCKOutcomeValue from ORKTaskResult

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 11. Make an ActiveTask Using ResearchKit. Next episode: 13. Create OCKDataSeriesConfiguration

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 12. Extract OCKOutcomeValue from ORKTaskResult

Part 3, Episode 13, Extract OCKOutcomeValue from ORKTaskResult

In this episode, I want to show you how to store the ORKTaskResult by creating OCKOutcomeValue.

As I mentioned earlier the schema of CareKitStore, out of each task, you have an outcome, but if you are using ResearchKit to have a survey, you have to save the outcome of the ResearchKit survey manually to the CareKitStore because there is no storage or anything available for ResearchKit to save the data.

Let’s jump to Xcode, open the SurveyViewModel from Project Navigator, and create the first function to extract checkInSurveyOutcome.

 static func checkInSurveyOutcome(_ result: ORKTaskResult) -> [OCKOutcomeValue]? {
  guard
    let response = result.results?
      .compactMap({ $0 as? ORKStepResult })
      .first(where: { $0.identifier == IdentifierModel.checkinForm.rawValue }),
    let scaleResults = response.results?
      .compactMap({ $0 as? ORKScaleQuestionResult }),
    let musclePainAnswer = scaleResults
      .first(where: { $0.identifier == IdentifierModel.checkinMuscle.rawValue })?
      .scaleAnswer,
    let headacheAnswer = scaleResults
      .first(where: { $0.identifier == IdentifierModel.checkinHeadache.rawValue })?
      .scaleAnswer,
    let tirednessAnswer = scaleResults
      .first(where: { $0.identifier == IdentifierModel.checkinTiredness.rawValue })?
      .scaleAnswer,
    let feverAnswer = scaleResults
      .first(where: { $0.identifier == IdentifierModel.checkinFever.rawValue })?
      .scaleAnswer,
    let nauseaAnswer = scaleResults
      .first(where: { $0.identifier == IdentifierModel.checkinNausea.rawValue })?
      .scaleAnswer
  else {
    assertionFailure("Failed to extract answers from check in survey!")
    return nil
  }

    var musclePainValue = OCKOutcomeValue(Double(truncating: musclePainAnswer))
    musclePainValue.kind = IdentifierModel.checkinMuscle.rawValue

    var headacheValue = OCKOutcomeValue(Double(truncating: headacheAnswer))
    headacheValue.kind = IdentifierModel.checkinHeadache.rawValue

    var tirednessValue = OCKOutcomeValue(Double(truncating: tirednessAnswer))
    tirednessValue.kind = IdentifierModel.checkinTiredness.rawValue

    var feverValue = OCKOutcomeValue(Double(truncating: feverAnswer))
    feverValue.kind = IdentifierModel.checkinFever.rawValue

    var nauseaValue = OCKOutcomeValue(Double(truncating: nauseaAnswer))
    nauseaValue.kind = IdentifierModel.checkinNausea.rawValue

    return [musclePainValue, headacheValue, tirednessValue, feverValue, nauseaValue]
  }

What you did here is your search for an identifier that you’ve assigned it to the survey for each step and then extract it from the result.

Next step is to make an OCKOutcomeValue based on the data you’ve extracted from the survey and finally make an array and return it.

Now let’s navigate to TaskViewModel and put this function for the CheckIn completion handler, find the checkIn case, then replace the extractOutcome value with this:

SurveyViewModel.checkInSurveyOutcome(_:)

You just used the function you’ve made as an outcome extractor, allowing CareKitStore to store the right value with the right data type and unique identifier.

Build and run; it should work as before; however, you store the value of the checkin survey somewhere now.

Next is to do the same process for the motion check task as well. Navigate to SurveyViewModel and add a new function to extract the range of motion data.

 static func rangeOfMotionSurveyOutcome(_ result: ORKTaskResult) -> [OCKOutcomeValue]? {
    guard
      let motionResult = result.results?
        .compactMap({ $0 as? ORKStepResult })
        .compactMap({ $0.results })
        .flatMap({ $0 })
        .compactMap({ $0 as? ORKRangeOfMotionResult })
        .first else {
      assertionFailure("Failed to parse range of motion result")
      return nil
    }
    var range = OCKOutcomeValue(motionResult.range)
    range.kind = #keyPath(ORKRangeOfMotionResult.range)
    return [range]
  }

Here you search for the ORKRangeOfMotionResult in a different way, which is a type; in some of the results, you might want to extract the data without knowing the identifier, then you can do this, especially your data is a range of something for example here is a range of motion.

Let’s use this and save the data in the CareKitStore. Navigate to TaskViewModel and then inside of motionCheck case replace the extractOutcome value with this:

SurveyViewModel.rangeOfMotionSurveyOutcome(_:)

You just used the function you’ve made as an outcome extractor, allowing CareKitStore to store the range of motion survey outcome.

Remove the app from your phone or simulator and then build and run and do all the steps to store the data in the CareKitStore in the following episodes; you’ll learn how to show those values in the overview tab.