Part 2, Episode 09, Create CheckIn task
In this episode, I want to show you how to make a daily Symptom & Measurement Tracker. You should be familiar with the flow by now. So let’s begin.
From the project navigator inside the ViewModels folder, open TaskManager, and after // Make CheckIn - CareKitTask make a CheckIn task.
static func makeCheckin() -> OCKTask {
let schedule = OCKSchedule.dailyAtTime(
hour: 8,
minutes: 0,
start: Date(),
end: nil,
text: nil
)
let task = OCKTask(
id: TaskModel.checkIn.rawValue,
title: "Check In",
carePlanUUID: nil,
schedule: schedule
)
return task
}
Here you just made a daily schedule for the CheckIn task to tell the OCKDailyPageViewController to show a task inside the calendar every day at 8 until the user gets the vaccination done.
Next, you have to add it to the OCKStoreManager, so open the SceneDelegate from the project navigator, and inside of the taskList array right after // Add CheckIn Task to the StoreManager comment, add the task that you just made:
let taskList = [TaskManager.makeOnboarding(),
TaskManager.makeVaccinationCheck(),
TaskManager.makeCheckin()]
The next step is to make the survey, from project navigator inside the ViewModels folder, open SurveyManager, and at the bottom of the class right after // Prepare CheckIn Survey - ResearchTask comment, add a function:
private static var musclePainItem: ORKFormItem {
let musclePainAnswerFormat = ORKAnswerFormat.scale(
withMaximumValue: 10,
minimumValue: 1,
defaultValue: 5,
step: 1,
vertical: false,
maximumValueDescription: "Very painful",
minimumValueDescription: "No pain")
let musclePainItem = ORKFormItem(
identifier: IdentifierModel.checkinMuscle.rawValue,
text: "How would you rate your muscle pain?",
answerFormat: musclePainAnswerFormat)
musclePainItem.isOptional = false
return musclePainItem
}
Here you made an ORKFormItem for the muscle pain to get the scale of pain from the user, and you set vertical to false to have a horizontal scaling from 1 to 5. Next item in the form is headache item, let’s create it:
private static var headacheItem: ORKFormItem {
let headacheAnswerFormat = ORKAnswerFormat.scale(
withMaximumValue: 10,
minimumValue: 1,
defaultValue: 5,
step: 1,
vertical: false,
maximumValueDescription: "Very painful",
minimumValueDescription: "No pain")
let headacheItem = ORKFormItem(
identifier: IdentifierModel.checkinHeadache.rawValue,
text: "How would you rate your headache?",
answerFormat: headacheAnswerFormat)
headacheItem.isOptional = false
return headacheItem
}
Here you made an ORKFormItem for the headache to get the scale of pain from the user, and you set vertical to false to have a horizontal scaling from 1 to 5. Next step is a tiredness item:
private static var tirednessItem: ORKFormItem {
let tirednessAnswerFormat = ORKAnswerFormat.scale(
withMaximumValue: 10,
minimumValue: 0,
defaultValue: 5,
step: 1,
vertical: false,
maximumValueDescription: nil,
minimumValueDescription: nil)
let tirednessItem = ORKFormItem(
identifier: IdentifierModel.checkinTiredness.rawValue,
text: "How would you rate your tiredness?",
answerFormat: tirednessAnswerFormat)
tirednessItem.isOptional = false
return tirednessItem
}
Here is the same as before, you made an ORKFormItem for the tiredness to get the scale from the user, and you set vertical to false to have a horizontal scaling from 0 to 10. Next is fever level:
private static var feverItem: ORKFormItem {
let feverAnswerFormat = ORKAnswerFormat.continuousScale(
withMaximumValue: 42,
minimumValue: 35,
defaultValue: 37,
maximumFractionDigits: 2,
vertical: true,
maximumValueDescription: "°C",
minimumValueDescription: "°C")
let feverItem = ORKFormItem(
identifier: IdentifierModel.checkinFever.rawValue,
text: "What is your body temprature?",
answerFormat: feverAnswerFormat)
feverItem.isOptional = false
return feverItem
}
Here you add another form item for fever, but this time it’s vertical, and you have a temperature as a scale in the centigrade. And last item is nausea item:
private static var nauseaItem: ORKFormItem {
let nauseaAnswerFormat = ORKAnswerFormat.scale(
withMaximumValue: 5,
minimumValue: 0,
defaultValue: 2,
step: 1,
vertical: false,
maximumValueDescription: nil,
minimumValueDescription: nil)
let nauseaItem = ORKFormItem(
identifier: IdentifierModel.checkinNausea.rawValue,
text: "How would you rate your nausea?",
answerFormat: nauseaAnswerFormat)
nauseaItem.isOptional = true
return nauseaItem
}
You add the same form item to get the scale from 0 to 5. Now it’s time to make a survey itself. add this function to make it ready:
static func checkInSurvey() -> ORKTask {
let formStep = ORKFormStep(
identifier: IdentifierModel.checkinForm.rawValue,
title: "Check In",
text: "Please answer the following questions.")
formStep.formItems = [tirednessItem, headacheItem, musclePainItem, feverItem, nauseaItem]
formStep.isOptional = false
let surveyTask = ORKOrderedTask(identifier: IdentifierModel.checkinStep.rawValue, steps: [formStep])
return surveyTask
}
Here you made an ORKFormStep and added all the form items into it. Next, you create an ORKOrderedTask and add only one step: a form step.
Now, in the project navigator inside the Models folder, open TaskModels and add a new case to the TaskModel right after // Add CheckIn Task comment:
case checkIn
So when you add this case, Xcode start complaining about usage of this enum in the TaskViewModel class, so open it from the project navigator inside of ViewModels folder and fulfill the switch case by adding the .checkIn case right after // Make CheckIn ViewController
case .checkIn:
let viewController = OCKSurveyTaskViewController(
taskID: TaskModel.checkIn.rawValue,
eventQuery: OCKEventQuery(for: date),
storeManager: storeManager,
survey: SurveyManager.checkInSurvey(),
extractOutcome: { _ in return [OCKOutcomeValue(Date())] })
viewController.surveyDelegate = delegate
listViewController.appendViewController(viewController, animated: false)
}
Here you make a viewController as a type of OCKSurveyTaskViewController and put the SurveyManager.checkInSurvey() as one of the inputs.
Next is to stop user to do the checkin tasks from the future also all other tasks so, add a varable like this to check the future and add it inside all the cases.
// Check the Date for future blocker
let isFuture = Calendar.current.compare(date, to: Date(), toGranularity: .day) == .orderedDescending
viewController.view.isUserInteractionEnabled = !isFuture
viewController.view.alpha = isFuture ? 0.4 : 1.0
You checked the date to see if the calendar date is in the future or not; just make sure you add these two lines for all the cases right after surveyDelegate.
The last step is to open TaskViewContoller and add a ViewController right after // Ask ViewModel to Make CheckIn ViewController comment:
TaskViewModel.checkIfInputTaskIsComplete(
input: .vaccinationCheck,
storeManager: self.storeManager) { vaccinationIsComplete in
if vaccinationIsComplete {
TaskViewModel.makeTaskViewController(
input: .checkIn,
date: date,
storeManager: self.storeManager,
listViewController: listViewController,
delegate: self)
}
}
You check if the vaccinationCheck task was completed, then you add a checkIn task to the list. Build and run the project to see how it looks, and just keep in mind, for now, you keep the storeManager on the memory, which means every time you run the app, the storeManager is empty, and you start fresh.