Comments (10)
Hello @InfuriatingYetti,
I'd be happy to help. If I'm understanding correctly, it seems that you're attempting to:
- present a task with one form within it
- based on the result of that form direct the user to another form thats within a different task (Choice2QuestionStep)
If so, this is different than the expected flow for a navigable task. All navigation should be based on steps within the same ORKTask. So in your case the flow would look something like this I believe:
- create a task (with steps: [choices1, choices2, completionStep]) and present it
- user answers the item in choices1
- at this point the user will either be directed to the choices2 step or skip it based on the predicates you passed in. (* as opposed to an entirely new task being presented)
If you take a look the ORKCatalog app in the TaskListRow.swift file there is a good example of a predicate being implemented for the "private var eligibilityTask: ORKTask" variable. That should definitely help demystify things. If I misunderstood anything or if you simply have more questions please let me know. Hope this helps!
from carekit.
Thank you so much for helping me out - it's a pleasure and thank you for the insight!! :) I checked out the TaskListRow.swift file in ResearchKit. From my understanding when using the ORKTask for approach (For example: TaskListRow.swift: steps += [questionStep3]), it will only show one question at a time, like in this example from the ResearchKit.org website (is that correct?):
Is it possible to navigate with this kind of set up below:
For example, if the user selects Severe
or maybe something other than None
it would either add additional questions to the two shown or open another set of questions like the two shown.
Thank you!
from carekit.
You're correct that you would have to show one page at a time for your use case. Conditional questions within one step isn't something ResearchKit supports. That does sound useful however.
from carekit.
This functionality would be very useful.
from carekit.
@pthealthtech @Pariecemckinney-apple
I was looking at the ORKOrderedTask+ORKPredefinedActiveTask.m
inside ResearchKit and see that there is a way to prompt an ORKInstructionStep
from an ORKTextChoice
. I think this compliments what @erik-apple did in the WWDC 21 for the Range of Motion
task using the options: [.excludeConclusion]
to show a customized ORKInstructionStep
:
From ORKOrderedTask+ORKPredefinedActiveTask.m
:
NSArray *textChoices = @[ [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE"],
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_2", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_2"],
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_3", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_3"],
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_4", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_4"],
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_5", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_5"],
[ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_6", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_6"] ];
ORKAnswerFormat *answerFormat2 = [ORKAnswerFormat valuePickerAnswerFormatWithTextChoices:textChoices];
ORKFormItem *formItem2 = [[ORKFormItem alloc] initWithSectionTitle:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_TITLE", nil) detailText:nil learnMoreItem:nil showsProgress:YES];
ORKFormItem *formItem3 = [[ORKFormItem alloc] initWithIdentifier:ORKTimedWalkFormAssistanceStepIdentifier
text:nil
answerFormat:answerFormat2];
formItem3.placeholder = ORKLocalizedString(@"TIMED_WALK_QUESTION_2_TEXT", nil);
formItem3.optional = NO;
step.formItems = @[formItem1, formItem2, formItem3];
step.optional = NO;
ORKStepArrayAddStep(steps, step);
}
With the ORKInstructionStep
:
` if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
{
ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
step.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"TIMED_WALK_INTRO_2_TEXT_%@", nil), formattedLength];
step.detailText = ORKLocalizedString(@"TIMED_WALK_INTRO_2_DETAIL", nil);
step.image = [UIImage imageNamed:@"timer" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
step.imageContentMode = UIViewContentModeCenter;
step.shouldTintImages = YES;
ORKStepArrayAddStep(steps, step);
}
}`
I was thinking, maybe you could change the ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier
to relate to a specific ORKTextChoice
answer response value(s)...from there would there be a way to modify the ORKInstructionStep
to show a second ORKTextChoice
form as opposed to just text??
Going back to what @erik-apple did in the WWDC 21, here is an example using ORKInstructionStep
for the kneeModel
(Range of Motion) in Survey.swift
using the options: [.excludeConclusion]:
, although, he just used text:
// MARK: Range of Motion.
static func rangeOfMotionCheck() -> ORKTask {
let rangeOfMotionOrderedTask = ORKOrderedTask.kneeRangeOfMotionTask(
withIdentifier: "rangeOfMotionTask",
limbOption: .left,
intendedUseDescription: nil,
options: [.excludeConclusion]
)
let completionStep = ORKCompletionStep(identifier: "rom.completion")
completionStep.title = "All done!"
completionStep.detailText = "We know the road to recovery can be tough. Keep up the good work!"
rangeOfMotionOrderedTask.appendSteps([completionStep])
return rangeOfMotionOrderedTask
}
static func extractRangeOfMotionOutcome(
_ 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]
}
// MARK: 3D Knee Model
static func kneeModel() -> ORKTask {
let instructionStep = ORKInstructionStep(
identifier: "insights.instructionStep"
)
instructionStep.title = "Your Injury Visualized"
instructionStep.detailText = "A 3D model will be presented to give you better insights on your specific injury."
instructionStep.iconImage = UIImage(systemName: "bandage")
let modelManager = ORKUSDZModelManager(usdzFileName: "toy_robot_vintage")
let kneeModelStep = ORK3DModelStep(
identifier: "insights.kneeModel",
modelManager: modelManager
)
let kneeModelTask = ORKOrderedTask(
identifier: "insights",
steps: [instructionStep, kneeModelStep]
)
return kneeModelTask
}
}
I don't know if it would be possible to mod the instruction step like this to show an ORKTextChoice
survey, what do you guys think? Could it be possible?!
Thank you!
from carekit.
I think I've got something going here!
Here is a form that is navigable to a second form - in the same window! After selecting the answer response in the first form and clicking Next
, and after selecting the answer in the second form and clicking Next
, you go to a completion step! ....also, all in the same window!
The problem I am having is while the code isn't giving me any errors, the code is not using logic to either skip the second question if I select None,
where it should take me to the completionStep.
, or to the next form if I select Severe.
I know it was said it couldn't be done, but I live outside the box and want to see how we can make this work :)
Under task.setNavigationRule(navigationRule1, forTriggerStepIdentifier: formStep1.identifier)
if I change formStep1. identifier
to completionStep.identifier
it will cause me to skip the second form and go from the first form to the completionStep
...what I am I missing here?!
import CareKitStore
import ResearchKit
struct ChoiceQuestions2 {
static let choiceIdentifier = "choice"
static let choiceFormIdentifier = "choice.form"
static let choiceQuestion1Step = "checkin.form.choicequestion1"
static let choiceStep = "checkin.form.Choice"
static let choice2Identifier = "choice2"
static let choice2FormIdentifier = "choice2.form"
static let choice2Question1Step = "checkin.form.choice2question1"
static let choice2Step = "checkin.form.Choice2"
static let choice2CompletionStepIdentifier = "choice2CompletionStep"
static func Choices2() -> ORKTask {
let Choices = [
ORKTextChoice(text: "None", value: 0 as NSNumber),
ORKTextChoice(text: "Slight", value: 1 as NSNumber),
ORKTextChoice(text: "Mild", value: 2 as NSNumber),
ORKTextChoice(text: "Severe", value: 3 as NSNumber)
]
let choiceAnswerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: Choices)
let choiceStep = ORKFormItem(identifier: choiceQuestion1Step, text: "Choices 2 Text question goes here", answerFormat: choiceAnswerFormat)
choiceStep.isOptional = false
let formStep = ORKFormStep(identifier: choiceFormIdentifier, title: "Choice Question", text: "Instructions for questions")
formStep.formItems = [choiceStep]
formStep.isOptional = false
let Choices2 = [
ORKTextChoice(text: "None", value: 0 as NSNumber),
ORKTextChoice(text: "Slight", value: 1 as NSNumber),
ORKTextChoice(text: "Mild", value: 2 as NSNumber),
ORKTextChoice(text: "Severe", value: 3 as NSNumber)
]
let choiceAnswerFormat2 = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: Choices2)
let choice2Step = ORKFormItem(identifier: choice2Question1Step, text: "Choices 1 Text question goes here", answerFormat: choiceAnswerFormat2)
choice2Step.isOptional = false
let form2Step = ORKFormStep(identifier: choice2FormIdentifier, title: "Choice Question", text: "Instructions for questions")
form2Step.formItems = [choice2Step]
form2Step.isOptional = false
let completionStep = ORKCompletionStep(identifier: choice2CompletionStepIdentifier)
completionStep.title = "Thank you!"
completionStep.text = "Your responses have been recorded."
let predicateFormat = ORKResultPredicate.predicateForChoiceQuestionResult(with: ORKResultSelector(resultIdentifier: choice2Question1Step), expectedAnswerValue: 3 as NSNumber)
let navigationRule = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: [(predicateFormat, choiceStep.identifier)], defaultStepIdentifierOrNil: completionStep.identifier)
let task = ORKNavigableOrderedTask(identifier: choice2Identifier, steps: [form2Step, formStep, completionStep])
task.setNavigationRule(navigationRule, forTriggerStepIdentifier: formStep.identifier)
return task
}
}
lol you all are great - thank you for the help so far!!!
from carekit.
This is the best I could come up with using a step navigable form. I am having trouble though passing the Value to CareKit. I think it's almost there, but need a little guidance please! :)
When I answer Question 1
as "Severe", value: 3
I am successfully navigated to Question 2
.
After selecting the an answer on Question 2
and press Next
I am navigated to completeStep
and once I click Done
I get the error: Thread 1: Fatal error: Failed to extract answers from check in survey!
; however, print("Result: \(result)")
under static func extractAnswersFromChoicesi(
is showing both answers from Question1
and Question 2
in the console. I selected answer Value: 3
and answer Value: 2
seen below:
If on Question 1
I select "None"
, "Slight"
, "Mild"
and press Next
I am navigated to completeStep
and once I click Done
, I again get the error: Thread 1: Fatal error: Failed to extract answers from check in survey!
; however, print("Result: \(result)")
under static func extractAnswersFromChoicesi(
is the answer from Question1
in the console. I selected answer Value: 2
seen below:
This is a mod from what @gavirawson-apple helped me with in the other tread and I tried adapting it to the navigable form. It's interesting to me because it seems that it is able to extract the answers from the form initially as I can get it to print (seen above), but it isn't able to process the data in this area:
guard
let choiceResult = result.results?
.compactMap({ $0 as? ORKStepResult })
.first(where: { $0.identifier == choiceFormIdentifier }),
let scale2Results = choiceResult
.results?
.compactMap({ $0 as? ORKChoiceQuestionResult }),
//1
let selectedChoices = scale2Results
.first(where: { $0.identifier == ChoiceQuestion1Step })?
// This line was the key!
.choiceAnswers as? [NSNumber],
//2
let selectedChoices2 = scale2Results
.first(where: { $0.identifier == ChoiceQuestion2Step })?
// This line was the key!
.choiceAnswers as? [NSNumber]
Here's the full code:
import CareKitStore
import ResearchKit
struct ChoiceQuestions3 {
static let choiceIdentifier = "choice"
static let choiceFormIdentifier = "choice.form"
static let ChoiceQuestion1Step = "checkin.form.choicequestion1"
static let ChoiceQuestion2Step = "checkin.form.choicequestion2"
static let choiceCompletionStep = "checkin.form.choiceCompletionStep"
static func Choices2() -> ORKTask {
var steps = [ORKStep]()
// Question 1
let question1 = ORKQuestionStep(identifier: ChoiceQuestion1Step)
question1.title = "Choices 1 Text question goes here"
let choices = [
ORKTextChoice(text: "None", value: 0 as NSNumber),
ORKTextChoice(text: "Slight", value: 1 as NSNumber),
ORKTextChoice(text: "Mild", value: 2 as NSNumber),
ORKTextChoice(text: "Severe", value: 3 as NSNumber)
]
question1.answerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: choices)
steps += [question1]
// Question 2
let question2 = ORKQuestionStep(identifier: ChoiceQuestion2Step)
question2.title = "Choices 2 Text question goes here"
question2.answerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: choices)
steps += [question2]
// Completion step
let completionStep = ORKCompletionStep(identifier: choiceCompletionStep)
completionStep.title = "Thank you!"
completionStep.text = "Your responses have been recorded."
steps += [completionStep]
// Navigation
let predicateFormat = ORKResultPredicate.predicateForChoiceQuestionResult(
with: ORKResultSelector(resultIdentifier: ChoiceQuestion1Step),
expectedAnswerValue: 3 as NSNumber
)
let navigationRule = ORKPredicateStepNavigationRule(
resultPredicatesAndDestinationStepIdentifiers:
[(predicateFormat, ChoiceQuestion2Step)],
defaultStepIdentifierOrNil: choiceCompletionStep
)
let task = ORKNavigableOrderedTask(identifier: choiceFormIdentifier, steps: steps)
task.setNavigationRule(navigationRule, forTriggerStepIdentifier: ChoiceQuestion1Step)
return task
}
static func extractAnswersFromChoicesi(
_ result: ORKTaskResult) -> [OCKOutcomeValue]? {
print("Result: \(result)") // <--- Is printing the correct selection from the ORKTextChoice selection
guard
let choiceResult = result.results?
.compactMap({ $0 as? ORKStepResult })
.first(where: { $0.identifier == choiceFormIdentifier }),
let scale2Results = choiceResult
.results?
.compactMap({ $0 as? ORKChoiceQuestionResult }),
//1
let selectedChoices = scale2Results
.first(where: { $0.identifier == ChoiceQuestion1Step })?
// This line was the key!
.choiceAnswers as? [NSNumber],
//2
let selectedChoices2 = scale2Results
.first(where: { $0.identifier == ChoiceQuestion2Step })?
// This line was the key!
.choiceAnswers as? [NSNumber]
else {
assertionFailure("Failed to extract answers from check in survey!")
return nil
}
//1
// Convert the choice values to CareKit outcome values
let outcomeValue = selectedChoices.map { selectedChoice -> OCKOutcomeValue in
var outcomeValue = OCKOutcomeValue(Double(truncating: selectedChoice))
// Set the kind here!
outcomeValue.kind = ChoiceQuestion1Step
return outcomeValue
}
//2
// Convert the choice values to CareKit outcome values
let outcomeValue2 = selectedChoices2.map { selectedChoice -> OCKOutcomeValue in
var outcomeValue2 = OCKOutcomeValue(Double(truncating: selectedChoice))
// Set the kind here!
outcomeValue2.kind = ChoiceQuestion2Step
return outcomeValue2
}
// makes an array style ie. [3.0, 2.0]
let outcomeTotal = outcomeValue + outcomeValue2
print()
print(outcomeValue)
print(outcomeValue2)
return outcomeTotal
}
Does anyone have any ideas??
Thank you!
from carekit.
An idea I had was maybe I need to somehow have each question step extract the data along the way as opposed to trying to extract it all at the end. The thought being, does each question step act as as it's own survey form (conceptually)...I messed around with it for a little bit last night trying to extract the data at each step, but wasn't able to get it work quite right still - had a bit of errors I'll have to figure out.
Theoretically, to get this to work would I need to extract each question's data along the way or is it possible to get it to extract all of the data at the end?
from carekit.
Hey @InfuriatingYetti! I wanted to circle back and see if you have reached a working solution yet. If not, I'm more than happy to keep working through this. Just let me know!
from carekit.
Hello @InfuriatingYetti, we still havenโt heard anything from you. Please let us know if you are are still stuck, otherwise we will close this issue soon.
from carekit.
Related Issues (20)
- Auto Scheduling Events HOT 3
- Warning when uploading App HOT 7
- Occurrence circles HOT 29
- Error when building on latest Xcode and macOS Ventura HOT 1
- Carekit_CareKitStore emitted errors HOT 1
- Compile Errors HOT 1
- How do you save an CareKit OCKOutcomeValue for an array of ResearchKit ORKChoiceQuestionResults HOT 12
- Bar chart changes every time i close and reopen the application HOT 9
- Problems integrating HealthKit data into an OCKCartesianChartViewController HOT 1
- CareKitStore-CoreData-NSReadOnlyPersistentStoreOption-ErrorMessage HOT 1
- Create monthly charts HOT 3
- Trying to schedule a notification based on the caloriesBurned only if they are less than a certain amount (with query to the health store) HOT 6
- Creating PDF with charts and data from more than one View HOT 1
- Sample app for v3? HOT 1
- CareKit live streaming crashes with multithreaded violations HOT 4
- Updated Content HOT 16
- Watch remote synchronization fails with duplicate Outcome.
- CareKit cannot compile with Xcode 15(beta 2) HOT 1
- Adding a detailed description to the nausea card HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from carekit.