CareKit is an open source software framework for creating apps that help people better understand and manage their health.

Overview

CareKit

CareKit

License Swift Xcode 11.0+ iOS 13.0+ SPM

CareKit™ is an open source software framework for creating apps that help people better understand and manage their health. The framework provides modules that can be used out of the box, or extended and customized for more targeted use cases. It is composed of three SPM packages which can each be imported separately.

  • CareKit: This is the best place to start building your app. CareKit provides view controllers that tie CareKitUI and CareKitStore together. The view controllers leverage Combine to provide synchronization between the store and the views.

  • CareKitUI: Provides the views used across the framework. The views are subclasses of UIView that are open and extensible. Properties within the views are public allowing for full control over the content.

  • CareKitStore: Provides a Core Data solution for storing patient data. It also provides the ability to use a custom store, such as a third party database or API.

Table of Contents

Requirements

The primary CareKit framework codebase supports iOS and requires Xcode 12.0 or newer. The CareKit framework has a Base SDK version of 13.0.

Getting Started

Installation (Option One): SPM

CareKit can be installed via SPM. Create a new Xcode project and navigate to File > Swift Packages > Add Package Dependency. Enter the url https://github.com/carekit-apple/CareKit and tap Next. Choose the main branch, and on the next screen, check off the packages as needed.

To add localized strings to your project, add the strings file to your project: English Strings

Installation (Option Two): Embedded Framework

Download the project source code and drag in CareKit.xcodeproj, CareKitUI.xcodeproj, and CareKitStore.xcodeproj as needed. Then, embed the framework as a dynamic framework in your app, by adding it to the Embedded Binaries section of the General pane for your target as shown in the figure below.

embedded-framework

OCKCatalog App

The included catalog app demonstrates the different modules that are available in CareKit: OCKCatalog

ockcatalog

OCKSampleApp

The included sample app demonstrates a fully constructed CareKit app: OCKSample

ocksample

CareKit

CareKit is the overarching package that provides view controllers to tie CareKitUI and CareKitStore together. When importing CareKit, CareKitUI and CareKitStore will be imported under the hood.

List view controllers

CareKit offers full screen view controllers for convenience. The view controllers query for and display data from a store, and stay synchronized with the data.

  • OCKDailyTasksPageViewController: Displays tasks for each day with a calendar to page through dates.

  • OCKContactsListViewController: Displays a list of contacts in the store.

Synchronized View Controllers

For each card in CareKitUI there is a corresponding view controller in CareKit. The view controllers are self contained modules that can be placed anywhere by using standard view controller containment. The view controller for each card leverages Combine to provide synchronization between the view and the store. This is done with the help of an OCKStoreManager, which wraps a store. When the store is modified, the store manager will publish notifications of the change. View controllers listen for this notification, and update the views accordingly.

To create a synchronized view controller:

// Create a store to hold your data.
let store = OCKStore(named: "my-store", type: .onDisk)

// Create a store manager to handle synchronization. Use this to instantiate all synchronized view controllers.
let storeManager = OCKSynchronizedStoreManager(wrapping: store)

// Create a view controller that queries for and displays data. The view will update automatically whenever the data in the store changes.
let viewController = OCKSimpleTaskViewController(taskID: "doxylamine", eventQuery: OCKEventQuery(for: Date()), storeManager: storeManager)

All synchronized view controllers have a controller and a view synchronizer. The controller is responsible for performing business logic. The view synchronizer defines how to instantiate the view to display, and how to update the view when the data in the store changes. Controllers and view synchronizers are customizable, and can be injected into a view controller to perform custom behavior:

// Define a custom view synchronizer.
class CustomSimpleTaskViewSynchronizer: OCKSimpleTaskViewSynchronizer {

    override func makeView() -> OCKSimpleTaskView {
        let view = super.makeView()
        // Customize the view when it is instantiated here...
        return view
    }

    override func updateView(_ view: OCKSimpleTaskView, context: OCKSynchronizationContext<OCKTaskEvents?>) {
        super.updateView(view, context: context)
        // Update the view when the data changes in the store here...
    }
}

// Define a custom controller.
class CustomSimpleTaskController: OCKSimpleTaskController {
    // Override functions here to provide custom business logic for the view controller...
}

// Instantiate the view controller with the custom classes, then fetch and observe data in the store.
let viewController = OCKSimpleTaskViewController(controller: CustomSimpleTaskController(storeManager: storeManager), viewSynchronizer: CustomSimpleTaskViewSynchronizer())
viewController.controller.fetchAndObserveEvents(forTaskID: "Doxylamine", eventQuery: OCKEventQuery(for: Date()))

Custom Synchronized View Controllers

CareKit supports creating a custom view that can be paired with a synchronized view controller to synchronize the custom view and the data in the store. In the sample code below, notice that the custom view conforms to OCKTaskDisplayable. This allows the view to notify the view controller when to perform certain business logic. This also means that when creating a custom view, you only need to call methods on OCKTaskDisplayable from the view and the view controller will automatically react:

// Define a custom view that displays an event for a task.
class TaskButton: UIButton, OCKTaskDisplayable {

    weak var delegate: OCKTaskViewDelegate?

    override init(frame: CGRect) {
        super.init(frame: frame)
        addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
    }

    @objc
    func didTap(_ sender: UIButton) {
        sender.isSelected.toggle()

        // Notify the view controller to mark the event for the task as completed.
        delegate?.taskView(self, didCompleteEvent: sender.isSelected, at: IndexPath(row: 0, section: 0), sender: sender)
    }
}

// Define a controller for the view to perform custom business logic.
class TaskButtonController: OCKTaskController {

    // This function gets called as a result of the delegate call in the view.
    func setEvent(atIndexPath indexPath: IndexPath, isComplete: Bool, completion: ((Result<OCKAnyOutcome, Error>) -> Void)?) {
        // Perform custom behavior here...
    }
}

// Define a view synchronizer for the custom view.
class TaskButtonViewSynchronizer: OCKTaskViewSynchronizerProtocol {

    typealias View = TaskButton

    // Instantiate the custom view.
    func makeView() -> TaskButton {
        return TaskButton(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
    }

    // Update the custom view when the data in the store changes.
    func updateView(_ view: TaskButton, context: OCKSynchronizationContext<OCKTaskEvents?>) {
        let event = context.viewModel?.firstEvent
        view.titleLabel?.text = event?.task.title
        view.isSelected = event?.outcome != nil
    }
}

// Finally, define a view controller that ties everything together.
class TaskButtonViewController: OCKTaskViewController<TaskButtonController, TaskButtonViewSynchronizer> {}

// Instantiate the view controller with the custom classes, then fetch and observe data in the store.
let viewController = TaskButtonViewController(controller: TaskButtonController(storeManager: storeManager), viewSynchronizer: TaskButtonViewSynchronizer())
viewController.controller.fetchAndObserveEvents(forTaskID: "Doxylamine", eventQuery: OCKEventQuery(for: Date()))

SwiftUI

A SwiftUI API is currently available for the InstructionsTaskView. The API is a starting point to demonstrate the API architecture. We would love to integrate community contributions that follow the API structure!

Similar to our UIKit API, there are two types of views - views in CareKitUI that can be initialized with static properties, and views in CareKit that are dynamic and update when data in the store changes.

To create a view in CareKit that is synchronized with the store:

// Create a controller that acts as an `ObservableObject` for the view.
let controller = OCKInstructionsTaskController(storeManager: storeManager)

// Fetch events for the Doxylamine task, and listen for changes to the task in the store.
controller.fetchAndObserveEvents(forTaskID: "doxylamine", eventQuery: OCKEventQuery(for: Date()))

// Create the view with the controller.
let view = InstructionsTaskView(controller: controller)

Data from the controller will be automatically mapped to the view. When the data in the controller changes, the view will update itself. You may want to customize the way in which data is mapped from the controller to the view. To do so, use this initializer on the CareKit view:

CareKit.InstructionsTaskView(controller: controller) { configuration in

    // Create an instance of a CareKitUI.InstructionsTaskView to render. Use the `configuration` for the default values to map to the view.
    CareKitUI.InstructionsTaskView(title: Text("Custom Title"),    // Inject custom text into the title here.
                                   detail: configuration.detail.map { Text($0) },
                                   instructions: configuration.instructions.map { Text($0) },
                                   isComplete: configuration.isComplete,
                                   action: configuration.action)

This initializer requires the initialization of a static SwiftUI view from CareKitUI. That opens up the customization points that are available in CareKitUI. See SwiftUI in CareKitUI

You can also create your own SwiftUI views that are synchronized with the store. CareKit controllers are compatible with SwiftUI and can help update the view when data in the store changes:

struct ContentView: View {

    // Observe the view model in the controller.
    @ObservedObject var controller: OCKSimpleTaskController

    // Define an event for convenience.
    var event: OCKAnyEvent? {
        controller.objectWillChange.value?.firstEvent
    }

    var body: some View {
        VStack(alignment: .center, spacing: 16) {
            Text(event?.task.title ?? "")
            Button(action: {
                let isComplete = self.event?.outcome != nil
                self.controller.setEvent(atIndexPath: IndexPath(row: 0, section: 0), isComplete: !isComplete, completion: nil)
            }) {
                self.event?.outcome != nil ? Text("Mark as Completed") : Text("Completed")
            }
        }
    }
}

CareKitUI

CareKitUI provides cards to represent tasks, charts, and contacts. There are multiple provided styles for each category of card.

All cards are built in a similar pattern, making it easy to recognize and customize the properties of each. They contain a headerView at the top that displays labels and icons. The contents of the card is placed inside a vertical contentStackView, allowing easy placement of custom views into a card without having to worry about breaking existing constraints.

For creating a card from scratch, see the OCKCardable protocol. Conforming to this protocol allows for styling a custom card to match the styling used across the framework.

Tasks

Below are the available task card styles:

Task

As an example, the instructions task card can be instantiated and customized like so:

let taskView = OCKInstructionsTaskView()

taskView.headerView.titleLabel.text = "Doxylamine"
taskView.headerView.detailLabel.text = "7:30 AM to 8:30 AM"

taskView.instructionsLabel.text = "Take the tablet with a full glass of water."

taskView.completionButton.isSelected = false
taskView.completionButton.label.text = "Mark as Completed"

Charts

Below are the available chart card styles:

Chart

As an example, the bar chart can be instantiated and customized like so:

let chartView = OCKCartesianChartView(type: .bar)

chartView.headerView.titleLabel.text = "Doxylamine"

chartView.graphView.dataSeries = [
    OCKDataSeries(values: [0, 1, 1, 2, 3, 3, 2], title: "Doxylamine")
]

Contacts

Below are the available contact card styles:

Contact

As an example, the simple contact card can be instantiated and customized like so:

let contactView = OCKSimpleContactView()

contactView.headerView.titleLabel.text = "Lexi Torres"
contactView.headerView.detailLabel.text = "Family Practice"

Styling

To easily provide custom styling or branding across the framework, see the OCKStylable protocol. All stylable views derive their appearance from a list of injected constants. This list of constants can be customized for quick and easy styling.

For example, to customize the separator color in a view and all of it's descendents:

// Define your custom separator color.
struct CustomColors: OCKColorStyler {
    var separator: UIColor { .black }
}

// Define a custom struct to hold your custom color.
struct CustomStyle: OCKStyler {
    var color: OCKColorStyler { CustomColors() }
}

// Apply the custom style to your view.
let view = OCKSimpleTaskView()
view.customStyle = CustomStyle()

Note that each view in CareKitUI is by default styled with OCKStyle. Setting a custom style on a view will propagate the custom style down to any subviews that do not already have a custom style set. The style propagation rules can be visualized in this diagram demonstrating three separate view hierarchies:

Styling

For information on styling SwiftUI views with OCKStylable, see SwiftUI in CareKitUI.

SwiftUI

A SwiftUI API is currently available for the InstructionsTaskView. The API is a starting point to demonstrate the API architecture. We would love to integrate community contributions that follow the API structure!

SwiftUI views in CareKitUI are static. To create a static view:

InstructionsTaskView(title: Text("Title"),
                     detail: Text("Detail"),
                     instructions: Text("Instructions"),
                     isComplete: false,
                     action: action)

Given that the initializer takes Text, you can apply custom modifiers to style the view:

InstructionsTaskView(title: Text("Title").font(Font.title.weight(.bold)),    // Apply custom modifiers here.
                     detail: Text("Detail"),
                     instructions: Text("Instructions"),
                     isComplete: false,
                     action: action)

The SwiftUI views are structured with generic containers in which custom content can be placed. The default versions of the views place content in the containers for you, but you can stray from the default behavior and inject custom content. For example, rather than using the default header, you can inject your own custom header:

InstructionsTaskView(isComplete: false,
                     instructions: Text("Instructions"),
                     action: action,
                     header: { CustomHeaderView() })    // Inject a custom header here.

You can also append or prepend custom views. To do so, wrap the default view and your custom views in a CardView. Both views will render inside of the same card:

CardView {
    InstructionsTaskView(/*...*/)
    CustomAppendedView()
}

Finally, SwiftUI views support styling with OCKStylable using a view modifier:

InstructionsTaskView(/*...*/)
    .careKitStyle(CustomStyle())

CareKitStore

The CareKitStore package defines the OCKStoreProtocol that CareKit uses to talk to data stores, and a concrete implementation that leverages CoreData, called OCKStore. It also contains definitions of most of the core structures and data types that CareKit relies on, such OCKAnyTask, OCKTaskQuery, and OCKSchedule.

Store

The OCKStore class is an append-only, versioned store packaged with CareKit. It is implemented on top of CoreData and provides fast, secure, on-device storage. OCKStore was designed to integrate with CareKit's synchronized view controllers, but can be used in isolation as well.

import CareKitStore

let store = OCKStore(named: "my-store", type: .onDisk)
let breakfastSchedule = OCKSchedule.dailyAtTime(hour: 8, minutes: 0, start: Date(), end: nil, text: "Breakfast")
let task = OCKTask(id: "doxylamine", title: "Doxylamine", carePlanID: nil, schedule: breakfastSchedule)

store.addTask(task) { result in
    switch result {
    case .failure(let error): print("Error: \(error)")
    case .success: print("Successfully saved a new task!")
    }
}

The most important feature of OCKStore is that it is a versioned store with a notion of time. When querying the store using a date range, the result returned will be for the state of the store during the interval specified. If no date interval is provided, all versions of the entity will be returned.

// On January 1st
let task = OCKTask(id: "doxylamine", title: "Take 1 tablet of Doxylamine", carePlanID: nil, schedule: breakfastSchedule)
store.addTask(task)

// On January 10th
let task = OCKTask(id: "doxylamine", title: "Take 2 tablets of Doxylamine", carePlanID: nil, schedule: breakfastSchedule)
store.updateTask(task)

// On some future date...

let earlyQuery = OCKTaskQuery(dateInterval: /* Jan 1st - 5th */)
store.fetchTasks(query: earlyQuery, callbackQueue: .main) { result in
    let title = try! result.get().first?.title 
    // "Take 1 Tablet of Doxylamine"
}

let laterQuery = OCKTaskQuery(dateInterval: /* Jan 12th - 17th */)
store.fetchTasks(query: laterQuery, callbackQueue: .main) { result in
    let title = try! result.get().first?.title 
    // "Take 2 Tablets of Doxylamine"
}

// Queries return the newest version of the task during the query interval!
let midQuery = OCKTaskQuery(dateInterval: /* Jan 5th - 15th */)
store.fetchTasks(query: laterQuery, callbackQueue: .main) { result in
    let title = try! result.get().first?.title 
    // "Take 2 Tablets of Doxylamine"
}

// Queries with no date interval return all versions of the task
let allQuery = OCKTaskQuery()
store.fetchTasks(query: allQuery, callbackQueue: .main) { result in 
    let titles = try! result.get().map { $0.title } 
    // ["Take 2 Tablets of Doxylamine", "Take 1 Tablet of Doxylamine"]
}

This graphic visualizes how results are retrieved when querying versioned objects in CareKit. Note how a query over a date range returns the version of the object valid in that date range.
3d608700-5193-11ea-8ec0-452688468c72

Schema

CareKitStore defines six high level entities as illustrated in this diagram:

Schema

  • Patient: A patient represents the user of the app.

  • Care Plan: A patient may have zero or more care plans. A care plan organizes the contacts and tasks associated with a specific treatment. For example, a patient may have one care plan for heart disease and a second for obesity.

  • Contact: A care plan may have zero or more associated contacts. Contacts might include doctors, nurses, insurance providers, or family.

  • Task: A care plan may have zero or more tasks. A task represents some activity that the patient is supposed to perform. Examples may include taking a medication, exercising, journaling, or checking in with their doctor.

  • Schedule: Each task must have a schedule. The schedule defines occurrences of a task, and may optionally specify target or goal values, such as how much of a medication to take.

  • Outcome: Each occurrence of a task may or may not have an associated outcome. The absence of an outcome indicates no progress was made on that occurrence of the task.

  • Outcome Value: Each outcome may have zero or more values associated with it. A value might represent how much medication was taken, or a plurality of outcome values could represent the answers to a survey.

It is important to note that tasks, contacts, and care plans can exist without a parent entity. Many CareKit apps are targeted to well defined use cases, and it can often be expedient to simply create tasks and contacts without defining a patient or care plan.

Scheduling

The scheduling tools provided in CareKit allow very precise and customizable scheduling of tasks. Each instance of an OCKSchedule is created by composing one or more OCKScheduleElements, which each define a single repeating interval.

Static convenience methods exist to help with common use cases.

let breakfastSchedule = OCKSchedule.dailyAtTime(hour: 8, minutes: 0, start: Date(), end: nil, text: "Breakfast")
let everySaturdayAtNoon = OCKSchedule.weeklyAtTime(weekday: 7, hours: 12, minutes: 0, start: Date(), end: nil)

Highly precise, complicated schedules can be created by combining schedule elements or other schedules.

// Combining elements to create a complex schedule
let elementA = OCKScheduleElement(start: today, end: nextWeek, interval: DateComponents(hour: 36))
let elementB = OCKScheduleElement(start: lastWeek, end: nil, interval: DateComponents(day: 2))
let complexSchedule = OCKSchedule(composing: [elementA, elementB])

// Combing two schedules into a composed schedule
let dailySchedule = OCKSchedule.dailyAtTime(hour: 8, minutes: 0, start: tomorrow, end: nextYear, text: nil)
let crazySchedule = OCKSchedule(composing: [dailySchedule, complexSchedule])

Schedules have a number of other useful properties that can be set, including target values, durations, and textual descriptions.

let element = OCKScheduleElement(
    start: today,  // The date and time this schedule will begin
    end: nextYear, // The date and time this schedule will end
    interval: DateComponents(day: 3), // Occurs every 3 days
    text: "Before bed", // "Before bed" will be show instead of clock time
    targetValues: [OCKOutcomeValue(10, units: "mL")], // Specifies what counts as "complete"
    duration: Duration = .hours(2) // The window of time to complete the task
)
  • text: By default, CareKit view controllers will prompt users to perform tasks using clock time (e.g. "8:00PM"). If you provide a text property, then the text will be used to prompt the user instead ("Before bed").

  • duration: If you provide a duration, CareKit will prompt the user to perform the scheduled task within a window (e.g. "8:00 - 10:00 PM"). The duration can also be set to .allDay if you do not wish to specify any time in particular.

  • targetValues: Target values are used by CareKit to determine if a user has completed a specific task or not. See OCKAdherenceAggregator for more details.

Custom Stores and Types

The OCKStore class provided with CareKit is a fast, secure, on-device store and will serve most use cases well. That said, we recognize it may not fully meet the needs of all our developers, so CareKit also allows you to write your own store. For example, you could write a wrapper around a web server, or even a simple JSON file. Any class that conforms to the OCKStoreProtocol can be used in place of the default store.

Writing a CareKit store adapter requires defining the entities that will live in your store and implementing asynchronous Create, Read, Update, and Delete methods for each. Stores are free to define their own types, as long as those types conform to a certain protocol. For example, if you are writing a store that can hold tasks, you might do it like this.

import CareKitStore

struct MyTask: OCKAnyTask & Equatable & Identifiable {

    // MARK: OCKAnyTask
    let id: String
    let title: String
    let schedule: String
    /* ... */

    // MARK: Custom Properties
    let difficulty: DifficultyRating
    /* ... */
}

struct MyTaskQuery: OCKAnyTaskQuery {

    // MARK: OCKAnyTaskQuery
    let ids: [String]
    let carePlanIDs: [String]
    /* ... */

    // MARK: Custom Properties
    let difficult: DifficultyRating?
}

class MyStore: OCKStoreProtocol {

    typealias Task = MyTask
    typealias TaskQuery = MyTaskQuery
    /* ... */

    // MARK: Task CRUD Methods
    func fetchTasks(query: TaskQuery, callbackQueue: DispatchQueue, completion: @escaping OCKResultClosure<[Task]>) { /* ... */ }
    func addTasks(_ tasks: [Task], callbackQueue: DispatchQueue, completion: OCKResultClosure<[Task]>?) { /* ... */ }
    func updateTasks(_ tasks: [Task], callbackQueue: DispatchQueue, completion: OCKResultClosure<[Task]>?) { /* ... */ }
    func deleteTasks(_ tasks: [Task], callbackQueue: DispatchQueue, completion: OCKResultClosure<[Task]>?) { /* ... */ }

    /* ... */
}

Using the four basic CRUD methods you supply, CareKit is able to use protocol extensions to imbue your store with extra functionality. For example, a store that implements the four CRUD methods for tasks automatically receives the following methods.

func fetchTask(withID id: String, callbackQueue: DispatchQueue, completion: @escaping OCKResultClosure<Task>)
func addTask(_ task: Task, callbackQueue: DispatchQueue, completion: OCKResultClosure<Task>?)
func updateTask(_ task: Task, callbackQueue: DispatchQueue, completion: OCKResultClosure<Task>?)
func deleteTask(_ task: Task, callbackQueue: DispatchQueue, completion: OCKResultClosure<Task>?)

Methods provided via protocol extensions employ naive implementations. As the developer, you are free to provide your own implementations that leverage the capabilities of your underlying data store to achieve greater performance or efficiency.

If you are considering implementing your own store, read over the protocol notes and documentation carefully.

Integrating with ResearchKit

CareKit and ResearchKit are sister frameworks and are designed to integrate well with one another.

When integrating a ResearchKit into your CareKit app, there are a series of steps you will need to follow.

  1. Subclass an existing task view controller
  2. Override the method that is called when the task is completed
  3. Present a ResearchKit survey and wait for the user to complete it
  4. Get the survey result and save it to CareKit's store

Here is an example demonstrating how to prompt the user to rate their pain on a scale of 1-10. Keep in mind as you're reading the code below that CareKit and ResearchKit both use the term "task", but that they are distinct.

// 1. Subclass a task view controller to customize the control flow and present a ResearchKit survey!
class SurveyViewController: OCKInstructionsTaskViewController, ORKTaskViewControllerDelegate {

    // 2. This method is called when the use taps the button!
    override func taskView(_ taskView: UIView & OCKTaskDisplayable, didCompleteEvent isComplete: Bool, at indexPath: IndexPath, sender: Any?) {

        // 2a. If the task was marked incomplete, fall back on the super class's default behavior or deleting the outcome.
        if !isComplete {
            super.taskView(taskView, didCompleteEvent: isComplete, at: indexPath, sender: sender)
            return
        }

        // 2b. If the user attempted to mark the task complete, display a ResearchKit survey.
        let answerFormat = ORKAnswerFormat.scale(withMaximumValue: 10, minimumValue: 1, defaultValue: 5, step: 1, vertical: false,
                                                 maximumValueDescription: "Very painful", minimumValueDescription: "No pain")
        let painStep = ORKQuestionStep(identifier: "pain", title: "Pain Survey", question: "Rate your pain", answer: answerFormat)
        let surveyTask = ORKOrderedTask(identifier: "survey", steps: [painStep])
        let surveyViewController = ORKTaskViewController(task: surveyTask, taskRun: nil)
        surveyViewController.delegate = self

        // 3a. Present the survey to the user
        present(surveyViewController, animated: true, completion: nil)
    }

    // 3b. This method will be called when the user completes the survey.
    func taskViewController(_ taskViewController: ORKTaskViewController, didFinishWith reason: ORKTaskViewControllerFinishReason, error: Error?) {        
        taskViewController.dismiss(animated: true, completion: nil)
        guard reason == .completed else {
            taskView.completionButton.isSelected = false
            return
        }

        // 4a. Retrieve the result from the ResearchKit survey
        let survey = taskViewController.result.results!.first(where: { $0.identifier == "pain" }) as! ORKStepResult
        let painResult = survey.results!.first as! ORKScaleQuestionResult
        let answer = Int(truncating: painResult.scaleAnswer!)

        // 4b. Save the result into CareKit's store
        controller.appendOutcomeValue(withType: answer, at: IndexPath(item: 0, section: 0), completion: nil)
    }
}

Once you have defined this view controller, you can add it into your app as you would any other CareKit view controller!

let todaysSurveyCard = SurveyViewController(
    taskID: "survey",
    eventQuery: OCKEventQuery(for: Date()),
    storeManager: storeManager)

present(surveyCard, animated: true, completion: nil)

You may also decide that you want the view to update to display the result of your survey instead of the default values used by the superclass. To change that, you can implement your own view synchronizer.

class SurveyViewSynchronizer: OCKInstructionsTaskViewSynchronizer {

    // Customize the initial state of the view
    override func makeView() -> OCKInstructionsTaskView {
        let instructionsView = super.makeView()
        instructionsView.completionButton.label.text = "Start Survey"
        return instructionsView
    }

    // Customize how the view updates
    override func updateView(_ view: OCKInstructionsTaskView,
                             context: OCKSynchronizationContext<OCKTaskEvents?>) {
        super.updateView(view, context: context)

        // Check if an answer exists or not and set the detail label accordingly
        if let answer = context.viewModel?.firstEvent?.outcome?.values.first?.integerValue {
            view.headerView.detailLabel.text = "Pain Rating: \(answer)"
        } else {
            view.headerView.detailLabel.text = "Rate your pain on a scale of 1 to 10"
        }
    }
}

Now, when you create an instance of your SurveyViewController, you can pass in your custom view synchronizer to change how the view updates.

let surveyCard = SurveyViewController(
    viewSynchronizer: SurveyViewSynchronizer(),
    taskID: "survey",                                             
    eventQuery: OCKEventQuery(date: Date()),
    storeManager: storeManager)

present(surveyCard, animated: true, completion: nil)

Getting Help

GitHub is our primary forum for CareKit. Feel free to open up issues about questions, problems, or ideas.

License

This project is made available under the terms of a BSD license. See the LICENSE file.

Comments
  • Monogram

    Monogram

    Added support for auto monogram generation according #10 I ported my func in objc and it picks the first and last word inside the name, so I removed also the assert for monogram and image not to be nil.

    The only issue I figured out is that if you put a title in front of the name it will pick that one.

    Eg.

    Dr. Maria Ruiz will be DR and not MR anymore.

    By the way the func check for monogram and activate the generator only it is nill.

    Dunno if is better to put a check for name to be forced now not to be nil.

    opened by matteocrippa 31
  • Outcomes sync results in data corruption

    Outcomes sync results in data corruption

    Hello,

    I have been working to convert to the new OCKRemoteSynchronization protocol and have run into a specific case which results in data corruption.

    The use case is that a user signs into a new device and the on-device store is synchronizing for the first time with the remote. The remote is returning a single OCKTask with five OCKOutcome entities. The five outcomes are associated with the task and are each newer versions (the previous UUIDs are correctly chained). The createdDate is the same for each of the outcomes but the updatedDate values are in the correct sequence. The entities in the revision passed in to merge() is sorted by updatedDate, which I believe is correct.

    After the synch is completed with the store, the event for the date in question, shows anywhere between 0 and 5 outcome values, which indicates that the on-device store is not applying all the full revision.

    There is a quick-n-dirty version of OCKSample that demonstrates this at https://github.com/nizar/CareKit/commit/2250f9a3e0a3195f4b549c23f97f308ecd7d21f8. The sample is set up with a OCKStore that uses a SimulatedRemote and corresponding SimulatedServer. The SimulatedServer simply returns the revision from a JSON string (below). To further simplify the sample, it uses the OCKDailyTasksPageViewController rather than the CareViewController from the standard OCKSample. Finally, the JSON was generated yesterday (March 15) so the task and outcomes will appear on that date if you run the sample. The correct result is a single event on March 15, with five outcome values. The displayed behavior is that the number of outcome values is often less than 5 and any additional taps to log new outcome values results in an error.

    The JSON for the remote revisions is: "{\"knowledgeVector\":{\"processes\":[{\"id\":\"4CE82DE2-F41F-490E-9F50-D9450BB0EF64\",\"clock\":12},{\"id\":\"65797806-E94D-4FDC-AE8A-74B81CD92900\",\"clock\":4},{\"id\":\"C0226289-7F38-4625-977A-C53A663F6E8E\",\"clock\":4}]},\"entities\":[{\"type\":\"task\",\"object\":{\"id\":\"nausea\",\"uuid\":\"0838965B-FCAB-44A8-A6D8-418E9F02BC4C\",\"nextVersionUUIDs\":[],\"schemaVersion\":{\"majorVersion\":2,\"minorVersion\":1,\"patchNumber\":0},\"createdDate\":637569158.4899869,\"tags\":[],\"updatedDate\":637569158.49000001,\"title\":\"Track your nausea\",\"notes\":[],\"previousVersionUUIDs\":[],\"timezone\":{\"identifier\":\"America\\/Los_Angeles\"},\"instructions\":\"Tap the button below anytime you experience nausea.\",\"impactsAdherence\":false,\"effectiveDate\":637142400,\"schedule\":{\"elements\":[{\"text\":\"Anytime throughout the day\",\"duration\":{\"isAllDay\":true},\"interval\":{\"minute\":0,\"hour\":0,\"second\":0,\"day\":1,\"month\":0,\"year\":0,\"weekOfYear\":0},\"targetValues\":[],\"start\":637142400}]}}},{\"type\":\"outcome\",\"object\":{\"schemaVersion\":{\"majorVersion\":2,\"minorVersion\":1,\"patchNumber\":0},\"nextVersionUUIDs\":[],\"uuid\":\"59B7CE13-C381-4300-A9B7-A8FB51325FF4\",\"createdDate\":637569161.6307621,\"tags\":[],\"updatedDate\":637569161.63076901,\"taskOccurrenceIndex\":4,\"values\":[{\"type\":\"boolean\",\"createdDate\":637569161.627213,\"value\":true}],\"notes\":[],\"taskUUID\":\"0838965B-FCAB-44A8-A6D8-418E9F02BC4C\",\"previousVersionUUIDs\":[],\"timezone\":{\"identifier\":\"America\\/Los_Angeles\"},\"effectiveDate\":637569161.62754393}},{\"type\":\"outcome\",\"object\":{\"schemaVersion\":{\"majorVersion\":2,\"minorVersion\":1,\"patchNumber\":0},\"nextVersionUUIDs\":[],\"uuid\":\"A06AC993-2A91-444A-8D36-2303BF7879AD\",\"createdDate\":637569161.6307621,\"tags\":[],\"updatedDate\":637569162.96640611,\"taskOccurrenceIndex\":4,\"values\":[{\"type\":\"boolean\",\"createdDate\":637569162.96377802,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569161.627213,\"value\":true}],\"notes\":[],\"taskUUID\":\"0838965B-FCAB-44A8-A6D8-418E9F02BC4C\",\"previousVersionUUIDs\":[\"59B7CE13-C381-4300-A9B7-A8FB51325FF4\"],\"timezone\":{\"identifier\":\"America\\/Los_Angeles\"},\"effectiveDate\":637569161.62754393}},{\"type\":\"outcome\",\"object\":{\"schemaVersion\":{\"majorVersion\":2,\"minorVersion\":1,\"patchNumber\":0},\"nextVersionUUIDs\":[],\"uuid\":\"E7B89B33-499C-4650-B4AE-CA647BD03602\",\"createdDate\":637569161.6307621,\"tags\":[],\"updatedDate\":637569164.12518001,\"taskOccurrenceIndex\":4,\"values\":[{\"type\":\"boolean\",\"createdDate\":637569162.96377802,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569164.122509,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569161.627213,\"value\":true}],\"notes\":[],\"taskUUID\":\"0838965B-FCAB-44A8-A6D8-418E9F02BC4C\",\"previousVersionUUIDs\":[\"A06AC993-2A91-444A-8D36-2303BF7879AD\"],\"timezone\":{\"identifier\":\"America\\/Los_Angeles\"},\"effectiveDate\":637569161.62754393}},{\"type\":\"outcome\",\"object\":{\"schemaVersion\":{\"majorVersion\":2,\"minorVersion\":1,\"patchNumber\":0},\"nextVersionUUIDs\":[],\"uuid\":\"5830D963-B790-431C-AF76-78A90D5CC793\",\"createdDate\":637569161.6307621,\"tags\":[],\"updatedDate\":637569165.34137106,\"taskOccurrenceIndex\":4,\"values\":[{\"type\":\"boolean\",\"createdDate\":637569162.96377802,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569161.627213,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569165.3385601,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569164.122509,\"value\":true}],\"notes\":[],\"taskUUID\":\"0838965B-FCAB-44A8-A6D8-418E9F02BC4C\",\"previousVersionUUIDs\":[\"E7B89B33-499C-4650-B4AE-CA647BD03602\"],\"timezone\":{\"identifier\":\"America\\/Los_Angeles\"},\"effectiveDate\":637569161.62754393}},{\"type\":\"outcome\",\"object\":{\"schemaVersion\":{\"majorVersion\":2,\"minorVersion\":1,\"patchNumber\":0},\"nextVersionUUIDs\":[],\"uuid\":\"E772CCD1-994F-440E-BDE2-F1103D1CD622\",\"createdDate\":637569161.6307621,\"tags\":[],\"updatedDate\":637569169.32398391,\"taskOccurrenceIndex\":4,\"values\":[{\"type\":\"boolean\",\"createdDate\":637569161.627213,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569164.122509,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569169.32103109,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569162.96377802,\"value\":true},{\"type\":\"boolean\",\"createdDate\":637569165.3385601,\"value\":true}],\"notes\":[],\"taskUUID\":\"0838965B-FCAB-44A8-A6D8-418E9F02BC4C\",\"previousVersionUUIDs\":[\"5830D963-B790-431C-AF76-78A90D5CC793\"],\"timezone\":{\"identifier\":\"America\\/Los_Angeles\"},\"effectiveDate\":637569161.62754393}}]}"

    I have attempted to dig through the CareKitStore codebase to see where this may be occurring, but no luck thus far. Any help of direction would be greatly appreciated.

    opened by nizar 30
  • Have a repeatable task - never marked as completed

    Have a repeatable task - never marked as completed

    Is is possible to have a repeatable task that is never marked as completed so that a patient may enter some event as often as they experience it in the same day?

    opened by scdi 30
  • Question about OCKOutcome, OCKOutcomeValue, OCKSchedule, and OCKScheduleEvent

    Question about OCKOutcome, OCKOutcomeValue, OCKSchedule, and OCKScheduleEvent

    It seems that OCKOutcome, OCKOutcomeValue, OCKSchedule, and OCKScheduleEvent are setup slightly different from OCKPatient, OCKCarePlan, OCKTask, etc. For example:

    1. OCKOutcome cannot be initialized with with an id and OCKStore creates one on it's own that looks something like, x-coredata://BC6D5810-5D8F-4E27-A74B-C33F32E19387/OCKCDTask/p22_7 which looks more like the localDatabaseID
    2. OCKOutcome, OCKOutcomeValue, OCKSchedule, and OCKScheduleEvent doesn't have an id at all
    3. OCKSchedule doesn't have a remoteId (I haven't needed this so far because I've only needed to sync OCKScheduleElement to the cloud)

    To get around the issues above, I use userInfo to add UUID's for each. Are id's missing, or in the case of OCKOutcome having it's id used in a different way setup like this for a particular reason?

    The reason I ask this because when syncing between local devices and cloud, if a local OCKStore entity doesn't have an id and it's remoteId isn't set, then I don't see a way to link the local and cloud entities

    opened by cbaker6 23
  • Bar charts only display up to 2 variables

    Bar charts only display up to 2 variables

    It seems that when one attempts to display more than 2 bar chart series in insights only the percents values show and not the graphic charts themselves.

    opened by scdi 23
  • CareKitStore Mystery Crash

    CareKitStore Mystery Crash

    Hi everyone, I’ve been struggling with a nasty crash in my app and was hoping someone could help identify the issue. We think this crash is occurring in the CareKitStore and whenever it happens the user enters a very bad crash loop, which makes their phone unusable until they restart it.

    Our team has recently integrated CareKit into our app which has both an iOS component and a watchOS component. Our iOS app has two main sections: a live heart rate monitoring section (where the heartrate is obtained from the apple watch) and a carekit view section (where we have a few customized cards and functionality). Historically, this crash has occurred in the background while live monitoring is in progress. I’ve attached a copy of the crash log we’ve been working with for reference. I’m also more than willing to share our code snippets if that helps. We’ve spent quite some time investigating this and we’d appreciate any advice you may be able to offer.

    Thanks!

    Date/Time:           2020-09-03 17:52:53.3923 +0400
    Launch Time:         2020-09-03 17:52:52.7130 +0400
    OS Version:          iPhone OS 13.5.1 (17F80)
    Release Type:        User
    Baseband Version:    5.60.01
    Report Version:      104
    
    Exception Type:  EXC_BREAKPOINT (SIGTRAP)
    Exception Codes: 0x0000000000000001, 0x00000001998b8fd0
    Termination Signal: Trace/BPT trap: 5
    Termination Reason: Namespace SIGNAL, Code 0x5
    Terminating Process: exc handler [823]
    Triggered by Thread:  0
    
    Thread 0 name:
    Thread 0 Crashed:
    0   libswiftCore.dylib            	0x00000001998b8fd0 _assertionFailure(_:_:file:line:flags:) + 796 (AssertCommon.swift:132)
    1   libswiftCore.dylib            	0x00000001998b8fd0 _assertionFailure(_:_:file:line:flags:) + 796 (AssertCommon.swift:132)
    2   Epilepsy Research Kit         	0x0000000100c0b59c _hidden#4064_ + 1288 (__hidden#4121_:0)
    3   Epilepsy Research Kit         	0x0000000100c0b5f0 _hidden#4065_ + 76 (__hidden#100_:0)
    4   CoreData                      	0x0000000190e7a854 -[NSPersistentStoreCoordinator _doAddPersistentStoreWithDescription:privateCopy:completeOnMainThread:withHandler:] + 968 (NSPersistentStoreCoordinator.m:1434)
    5   CoreData                      	0x0000000190d32cc4 -[NSPersistentStoreCoordinator addPersistentStoreWithDescription:completionHandler:] + 212 (NSPersistentStoreCoordinator.m:1478)
    6   CoreData                      	0x0000000190f37550 -[NSPersistentContainer _loadStoreDescriptons:withCompletionHandler:] + 228 (NSPersistentContainer.m:233)
    7   CoreData                      	0x0000000190d3c068 -[NSPersistentContainer loadPersistentStoresWithCompletionHandler:] + 312 (NSPersistentContainer.m:220)
    8   Epilepsy Research Kit         	0x0000000100c0b048 _hidden#1479_ + 1004 (__hidden#4121_:84)
    9   Epilepsy Research Kit         	0x0000000100bfb554 __hidden#1838_ + 4 (__hidden#3214_:99)
    10  Epilepsy Research Kit         	0x0000000100bfb554 OCKStore.context.getter + 56 (__hidden#3214_:103)
    11  Epilepsy Research Kit         	0x0000000100c0ef7c __hidden#1835_ + 4 (__hidden#100_:0)
    12  Epilepsy Research Kit         	0x0000000100c0ef7c _hidden#4323_ + 48 (__hidden#4589_:56)
    13  Epilepsy Research Kit         	0x0000000100c3ff60 OCKReadableTaskStore.fetchAnyTasks(query:callbackQueue:completion:) + 280 (__hidden#6322_:161)
    14  Epilepsy Research Kit         	0x0000000100b47c38 OCKStore.populateSampleData() + 3088 (OCKStoreExtension.swift:52)
    15  Epilepsy Research Kit         	0x0000000100ab3d8c closure #1 in AppDelegate.synchronizedStoreManager.getter + 360 (AppDelegate.swift:37)
    16  Epilepsy Research Kit         	0x0000000100ab9aa4 synchronizedStoreManager.get + 4 (AppDelegate.swift:34)
    17  Epilepsy Research Kit         	0x0000000100ab9aa4 CareKitStoreManager.init() + 240 (CareKitStoreManager.swift:24)
    18  Epilepsy Research Kit         	0x0000000100ab99a0 globalinit_33_B23A0DFC24CB2FF4B5C6BED9AECC1F71_func7 + 36 (CareKitStoreManager.swift:0)
    19  libdispatch.dylib             	0x000000018c0d3524 _dispatch_client_callout + 16 (object.m:495)
    20  libdispatch.dylib             	0x000000018c0a6364 _dispatch_once_callout + 28 (once.c:52)
    21  libswiftCore.dylib            	0x0000000199b774d4 swift_once + 44 (once.h:111)
    22  Epilepsy Research Kit         	0x0000000100ae8590 mainContentView(stateView:) + 196 (CareKitStoreManager.swift:17)
    23  Epilepsy Research Kit         	0x0000000100aeb5bc closure #1 in closure #1 in MirrorHRView.body.getter + 1372 (StartHere - ContentView.swift:85)
    24  Epilepsy Research Kit         	0x0000000100aeaf0c closure #1 in MirrorHRView.body.getter + 408 (<compiler-generated>:0)
    25  Epilepsy Research Kit         	0x0000000100aef3b8 partial apply for closure #1 in MirrorHRView.body.getter + 16 (<compiler-generated>:0)
    26  SwiftUI                       	0x00000001c3d6e2a8 GeometryReader.Child.update(context:) + 396 (GeometryReader.swift:79)
    27  SwiftUI                       	0x00000001c3d6e500 protocol witness for static UntypedAttribute._update(_:graph:attribute:) in conformance GeometryReader<A>.Child + 36 (<compiler-generated>:0)
    28  AttributeGraph                	0x00000001b7e59998 partial apply + 28 (<compiler-generated>:0)
    29  AttributeGraph                	0x00000001b7e429fc AG::Graph::UpdateStack::update() + 440 (ag-closure.h:104)
    30  AttributeGraph                	0x00000001b7e42eb4 AG::Graph::update_attribute(unsigned int, bool) + 372 (ag-graph-update.cc:399)
    31  AttributeGraph                	0x00000001b7e47c30 AG::Subgraph::update(unsigned int) + 688 (ag-subgraph.cc:517)
    32  SwiftUI                       	0x00000001c3c5e1dc ViewGraph.runTransaction(in:) + 212 (ViewGraph.swift:716)
    33  SwiftUI                       	0x00000001c3c5e524 closure #1 in ViewGraph.updateOutputs(at:) + 116 (ViewGraph.swift:641)
    34  SwiftUI                       	0x00000001c3c5e290 ViewGraph.updateOutputs(at:) + 120 (<compiler-generated>:0)
    35  SwiftUI                       	0x00000001c3efde10 closure #1 in closure #1 in ViewRendererHost.render(interval:updateDisplayList:) + 816 (ViewRendererHost.swift:157)
    36  SwiftUI                       	0x00000001c3efd2b0 closure #1 in ViewRendererHost.render(interval:updateDisplayList:) + 524 (ViewRendererHost.swift:145)
    37  SwiftUI                       	0x00000001c3ef41b8 ViewRendererHost.render(interval:updateDisplayList:) + 316 (ViewRendererHost.swift:132)
    38  SwiftUI                       	0x00000001c3b90240 closure #1 in _UIHostingView.requestImmediateUpdate() + 68 (_UIHostingView.swift:165)
    39  SwiftUI                       	0x00000001c3b8cff8 thunk for @escaping @callee_guaranteed () -> () + 24 (<compiler-generated>:0)
    40  libdispatch.dylib             	0x000000018c0d29a8 _dispatch_call_block_and_release + 24 (init.c:1408)
    41  libdispatch.dylib             	0x000000018c0d3524 _dispatch_client_callout + 16 (object.m:495)
    42  libdispatch.dylib             	0x000000018c0b66fc _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 860 (inline_internal.h:2484)
    43  CoreFoundation                	0x000000018c38b7fc __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 (CFRunLoop.c:1749)
    44  CoreFoundation                	0x000000018c3866d0 __CFRunLoopRun + 1724 (CFRunLoop.c:3069)
    45  CoreFoundation                	0x000000018c385ce8 CFRunLoopRunSpecific + 424 (CFRunLoop.c:3192)
    46  GraphicsServices              	0x00000001964d038c GSEventRunModal + 160 (GSEvent.c:2246)
    47  UIKitCore                     	0x00000001904b4444 UIApplicationMain + 1932 (UIApplication.m:4823)
    48  Epilepsy Research Kit         	0x0000000100aa7aa0 main + 68 (SCIGenericWrapper.swift:18)
    49  libdyld.dylib                 	0x000000018c20d8f0 start + 4
    
    
    
    opened by coleramos425 22
  • CareKit Bridge Documentation

    CareKit Bridge Documentation

    Is there any documentation around how to incorporate CareKit bridge in an existing project to a custom cloud backend? The WWDC session only talked about Medable and did not link to any resources that describe how to build a custom solution.

    documentation 
    opened by SNagra 21
  • Update Package.swift file

    Update Package.swift file

    This makes it work when importing using a Package.swift file below:

    // swift-tools-version:5.3
    // The swift-tools-version declares the minimum version of Swift required to build this package.
    
    import PackageDescription
    
    let package = Package(
        name: "ParseCareKit",
        platforms: [.iOS(.v13), .watchOS(.v6), .macOS(.v10_13), .tvOS(.v11)],
        products: [
            .library(
                name: "ParseCareKit",
                targets: ["ParseCareKit"])
        ],
        dependencies: [
            // Dependencies declare other packages that this package depends on.
            .package(name: "CareKit", url: "https://github.com/cbaker6/CareKit.git",
                     .revision("d4fca9a3a61d8d3fb693329254a138613dbb9837")),
            .package(name: "ParseSwift", url: "https://github.com/parse-community/Parse-Swift", from: "1.1.2")
        ],
        targets: [
            .target(
                name: "ParseCareKit",
                dependencies: ["ParseSwift", .product(name: "CareKitStore", package: "CareKit")]),
            .testTarget(
                name: "ParseCareKitTests",
                dependencies: ["ParseCareKit"])
        ]
    )
    

    Still doesn't work through Xcode though, File > Swift Packages > Add Package Dependency...

    Note there are some warnings from the 5.3 tools for explicitly including other resource files. Fix #542

    opened by cbaker6 20
  • V2 interoperability with current ResearchKit

    V2 interoperability with current ResearchKit

    I’m at the point of wanting to add pain score questions to my V2 app. The old V1 sample showed how to do this by building an ORKOrderedTask. Attempting the same approach with V2 CareKit fails:

    Cannot convert value of type 'ORKOrderedTask' to expected element type 'OCKTask'

    Am I taking the wrong design approach or is the integration with ResearchKit not available yet?

    Thank you for your help, Jonathan

    opened by jwaddilove 19
  • Added selected and deselected text properties to OCKCareCardButton

    Added selected and deselected text properties to OCKCareCardButton

    This pull request probably needs some extra input before it's ready to merge. It's purpose is to add an optional label below OCKCareCardButton, but there is room for improvement in the way that data is passed to the cells.

    Currently I am passing the text in through userInfo, but this doesn't seem like a good way to make it a standard feature. If you have a preference for how to get the data to the button objects in a less hacky way, I'd be happy to update this PR.

    opened by moppymopperson 18
  • Add visualGroupIdentifier to OCKCarePlanActivity

    Add visualGroupIdentifier to OCKCarePlanActivity

    Resolves #170 by adding a new property on OCKCarePlanActivity, visualGroupIdentifier. Activities grouped with the same visualGroupIdentifier will appear together in the Care Contents card. The documentation note also explicitly mentions that this behavior is overridden if the activity is optional or read-only. This preserves the groupIdentifier field to group activities for querying in the care plan store, as its original intention was.

    Specific changes in this PR:

    • Added the visualGroupingIdentifier property on OCKCarePlanActivity.
    • Updated the backing Core Data model with a new model version (v3) that includes the visualGroupIdentifier.
    • Updated OCKCareContentsViewController to use visualGroupIdentifier instead of groupIdentifier to form the tableView sections.
    • Updated sample project to use visualGroupIdentifier, showing two different visual groupings - Assessments and Physical Activity.
    • Updated tests to use initializer with visualGroupIdentifier.
    enhancement 
    opened by UberJason 18
  • Compile Errors

    Compile Errors

    I have looked through the other compile errors and can't find this one. I also get this with the recover App. The apps do build and run though 1

    These errors don't show if using the "stable" version of Carekit but then I don't have access to the OCKSurveyTaskViewController

    opened by glizan 1
  • Occurrence circles

    Occurrence circles

    Hi @gavirawson-apple,

    We are facing a very strange issue regarding occurrence circles (scheduling events). Sometimes the occurrence circles do not display in the app and it's not happening to all users and all devices. Some of our users are experiencing this issue and it started happening a few days ago.

    We've tested on the following real devices and we haven't experienced this issue:

    • iPhone XS (iOS 16.1)
    • iPhone 11 Pro Max (iOS 16.1)
    • iPhone 12 (iOS 15.7)
    • iPhone 12 Pro Max (iOS 16.0.3)
    • iPhone 13 Pro Max (iOS 15.7)

    But the people who are experiencing this issue are using:

    • iPhone 12 Pro Max (iOS 16.1)
    • iPhone 13 (iOS 16.1)

    Please see the attached screenshot: circles-issue

    Do you've any clue why it is happening to some of the users?

    Kind Regards,

    • Majid S.
    opened by majidsaleem105 24
  • Warning when uploading App

    Warning when uploading App

    Hi,

    recently I get warnings when uploading my App to AppStore Connect. The logs say:

    The app references non-public selectors in Payload/APPNAME.app/APPNAME: dismissViewController

    I don't have a function with that name in my app. Upon further investigation I found that CareKit implements such function in the OCKContactViewController and it is private.

    I only got the warning after switching to Xcode 14. Is it possible to make that function public to resolve the warning?

    opened by macintosh-HD 7
  • Auto Scheduling Events

    Auto Scheduling Events

    Hello @gavirawson-apple,

    We're trying to schedule some tasks so that they only appear for a specific time on the app side. For example, we want people to see a task at 08 o'clock in the morning and remove that task from the app at 09 o'clock. So, it should be displayed before 8 o'clock and after 9 o'clock during that day.

    I tried to achieve the goal using the surveySchedule = OCKScheduleElement(start: thisMorning!, end: expiryDate!, interval: DateComponents(hour: 1) ) Where thisMorning = 2022-10-07 08:00:00, and expiryDate = 2022-10-07 09:00:00

    But it didn't work as expected. It appears in the app all the time.

    Please advise me on how I can achieve my goal.

    Thank You,

    • Majid S.
    opened by majidsaleem105 3
  • Extra event for OCKTask with all day duration

    Extra event for OCKTask with all day duration

    Consider the following code snippet

    let taskStartDate = Calendar.current.startOfDay(for: Date())
    let taskEndDate = Calendar.current.date(byAdding: .day, value: 1, to: taskStartDate)
    let taskScheduleElement = OCKScheduleElement(
        start: taskStartDate, 
        end: taskEndDate, 
        interval: DateComponents(day: 1), 
        text: nil, 
        targetValues: [], 
        duration: .allDay
    ) 
    let taskSchedule = OCKSchedule(composing: [taskScheduleElement])
    let allDayTask = OCKTask(
        id: "allDayTask",
        title: "All day task", 
        carePlanUUID: nil, 
        schedule: taskSchedule
    ) 
    

    When this task is added to the store, it could be fetched on two consecutive days (today and tomorrow). For the 2nd day particularly, the result variable in the lines below have schedule element past the user specified time, but it won't be filtered out and therefore a task view controller can be created.

    https://github.com/carekit-apple/CareKit/blob/8ca7962d98654ab45cc2976218ffac2728a4a2b0/CareKitStore/CareKitStore/CoreData/OCKStore%2BTasks.swift#L47-L52

    However, if the user taps the completion button for the 2nd day, the program will crash when attempts to unwrap a nil value

    https://github.com/carekit-apple/CareKit/blob/8ca7962d98654ab45cc2976218ffac2728a4a2b0/CareKitStore/CareKitStore/Structs/OCKSchedule.swift#L60-L62

    One can find the nil value is set at line 176 below due to the >= operation

    https://github.com/carekit-apple/CareKit/blob/8ca7962d98654ab45cc2976218ffac2728a4a2b0/CareKitStore/CareKitStore/Structs/OCKScheduleElement.swift#L175-L180

    I understand that it is better to set the end time for the task schedule like this

    let taskEndDate = Calendar.current.date(byAdding: DateComponents(day: 1, second: -1), to: taskStartDate)
    
    

    and then the task will not be fetched on the 2nd day. However, I think there is some inconsistent treatments on the end date of the schedule. If at one place >= operation is considered to eliminate the intersection of a single time point of duration 0, then the code should not create any event for the task in the other place.

    I hope the code above is clear enough and easy to reproduce. Just in case, a toy example is available also at https://github.com/zhang-bo-lilly/TaskOccurenceIssue.git

    opened by zhang-bo-lilly 0
Releases(2.0.1)
  • 2.0.0(Dec 5, 2019)

    CareKit 2.0 Release Notes

    CareKit 2.0 supports iOS and requires Xcode 11.0 or later. The minimum supported Base SDK is 13.0. CareKit 2.0 includes the following new features and enhancements by Apple Inc. (https://github.com/carekit-apple)

    • New Architecture

    CareKit 2.0 has been rewritten from the ground up entirely in Swift 5. The new architecture has been reconstructed to allow for maximum customization and modularity. Splitting the framework out into various layers now enables users to tailor-make their care apps to the desired look and feel for their users.

    • CareKitUI

    Now a whole new separate project inside the CareKit repository, CareKitUI can be compiled and imported as a stand-alone framework without any dependencies on CareKit or the CareKitStore. CareKitUI consists of prepackaged views that developers can easily embed anywhere within their apps and populate it with personalized content.

    • CareKitStore

    Just like CareKitUI, the CareKitStore too can be imported in as a stand-alone framework without any dependencies. Built as a wrapper on top of CoreData, the CareKitStore offers an on-device datastore that is exclusive to your app. It comes with a predefined schema that facilitates persistence of Care Plans and associated Tasks. Developers can choose to update and modify the schema depending on their individual use cases.

    • CareKit

    CareKit, the framework, imports CareKitUI and CareKitStore under the hood. CareKit is all about providing complete synchronization between the UI and the underlying database and this synchronization is powered by the all new Combine feature available in Swift 5. Another key highlight of CareKit is its modularity. This modularity gives developers the freedom to utilize the CareKitStore or plug-in their own custom store that conforms to the OCKStore protocol in order to leverage the synchronization functionality for free. Additionally, developers can now use custom views or inject custom views that conform to our protocols to get the synchronization functionality at no cost.

    • Tasks

    The two entities previously know as Assessments and Interventions have been collapsed into a single Task entity. Tasks can be displayed in cards using any of a number of styles. Task card styles exist for a number of common use cases, simple tasks that only need to be performed once, tasks that should be performed several times, and tasks that can be performed any number of times.

    • Charts

    CareKit offers views and view controllers that display charts. The views can be styled and filled with custom data, and support multiple data series. The view controllers fetch and display data in the view, and update the view when the data changes. CareKit currently supports line, scatter, and bar charts.

    • Contacts

    CareKit offers views and view controllers that display contacts. The views can be styled and filled with custom data. The view controllers fetch and display data in the view, and update the view when the data changes. CareKit currently supports a simple and detailed contact view.

    • List View Controllers

    CareKit provides higher order view controllers that easily fetch and display data in a store. The first is a view controller that displays tasks and completion for a given day in the week. The second is a list of contacts.

    • Styling

    All CareKit views can be easily styled for a custom user interface by injecting a list of style constants. Views inherit the style of their parent, making it easy to quickly style or brand an app.

    • Updated Database Schema

    The database schema has been updated to handle new care-centric data types. The store now models Patients, Care Plans, Contacts, Tasks, Schedules, Outcomes, Outcome Values, and Notes. The store has been reinvented as an append only versioned database, and now allows you to keep a fully versioned history of key records.

    • Scheduling

    Task scheduling has been greatly improved in CareKit 2.0. It is now possible to create arbitrarily complex and precise schedules.

    • Sample App

    The Sample App (OCKSample project in CareKit's workspace) serves as a template application that combines different modules from the CareKit framework. It's a great starting point for your own CareKit apps.

    • Catalog App

    The Catalog App (OCKCatalog project in CareKit's workspace) showcases the views and controllers available in CareKit. It's an excellent reference for the visual building blocks available in CareKit, and is useful for designers and engineers who want to know what building blocks are available to them.

    Source code(tar.gz)
    Source code(zip)
Owner
CareKit
CareKit is an open source software framework for creating apps that help people better understand and manage their health.
CareKit
A platform where NYUAD students can both Help and Seek Help.

A platform where NYUAD students can both Help and Seek Help.

Omar Rayyan 0 Nov 7, 2021
Smooch is the best way to have personal, rich conversations with people on your website or customers on any device

Smooch is the best way to have personal, rich conversations with people on your website or customers on any device. Our features, integrations and developer-friendly APIs empower companies to connect with their customers in a whole new way.

Zendesk 121 Aug 1, 2022
Project Flat iOS is the iOS client of Agora Flat open source classroom.

Agora Flat iOS Project flat is the iOS client of Agora Flat open source classroom. 中文 Features Open sourced front-end and back-end [Flat Web][flat-web

netless 24 Dec 12, 2022
An open-source task management app for daily general operations

Taskaholic An open-source task management app for daily general operations, sepa

Aiden 1 Sep 19, 2022
Open-source API Client for iOS, iPadOS, macOS. Built with SwiftUI

Yogu Open-source API Client for iOS, iPadOS, macOS. Built with SwiftUI ?? Yogu is currently in development, and not actually usable yet. Please DO NOT

Beomjun Gil 5 Oct 29, 2022
A simple to use iOS/tvOS/watchOS SDK to help get you off the ground quickly and efficiently with your Elastic Path Commerce Cloud written in Swift.

Elastic Path Commerce Cloud iOS Swift SDK A simple to use iOS/tvOS/watchOS SDK to help get you off the ground quickly and efficiently with your Elasti

Moltin 36 Aug 1, 2022
A library for creating Stream Deck plugins in Swift.

StreamDeck A library for creating Stream Deck plugins in Swift. Usage Your plugin class should inherit from StreamDeckPlugin, which handles the WebSoc

Emory Dunn 15 Jan 2, 2023
Doll - A mac app to help monitor your app badges

Doll is a Mac app that help you catch up important messages! In macOS, It feels

null 328 Jan 4, 2023
Alter SDK is a cross-platform SDK consisting of a real-time 3D avatar system, facial motion capture, and an Avatar Designer component built from scratch for web3 interoperability and the open metaverse.

Alter SDK is a cross-platform SDK consisting of a real-time 3D avatar system, facial motion capture, and an Avatar Designer component built from scratch for web3 interoperability and the open metaverse.

Alter 45 Nov 29, 2022
PayByBank SDK is an alternative and easier form of Open Banking solutions.

PayByBank SDK (iOS) The Ecospend Gateway presents PayByBank SDK as an alternative and easier form of Open Banking Instant Payment solutions. PayByBank

Ecospend Technologies Limited 8 Oct 10, 2022
LibreSDK - Open FreeStyle Libre

LibreSDK - Open FreeStyle Libre Since there are a dozen of repositories floating around on github trying to decode the contents of a FreeStyle Libre.

Julian Groen 0 Feb 6, 2022
MbientLab 2 Feb 5, 2022
Install and debug iPhone apps from the command line, without using Xcode

ios-deploy Install and debug iOS apps from the command line. Designed to work on un-jailbroken devices. Requirements macOS You need to have a valid iO

null 3k Jan 8, 2023
In-app feedback and bug reporting tool for apps.

Instabug iOS SDK Instabug is an in-app feedback and bug reporting tool for mobile apps. With just a simple shake, your users or beta testers can repor

Instabug 274 Dec 14, 2022
A Demo using Vision Framework building on Core ML Framework

Core-ML-Sample A Demo using Core ML, Vision Framework and Swift 4. This demo is based on Inception V3 network. You must run it with Xcode 9 and iOS 11

杨萧玉 215 Nov 9, 2022
The official CovPass(-Check) iOS apps

CovPass / CovPass Check Einfach. Sicher. Papierlos. Mit der CovPass-App können Bürgerinnen und Bürger ihre Corona-Impfungen direkt auf das Smartphone

null 96 Dec 18, 2022
150,000+ stickers API & SDK for iOS Apps.

English | 한국어 Stipop UI SDK for iOS Stipop SDK provides over 150,000 .png and .gif stickers that can be easily integrated into mobile app chats, comme

Stipop, Inc. 19 Dec 20, 2022
Used to integrate the Facebook Platform with your iOS & tvOS apps.

Facebook SDK for iOS This open-source library allows you to integrate Facebook into your iOS app. Learn more about the provided samples, documentation

Meta 7.3k Jan 3, 2023
1Password Extension for iOS Apps

Retirement As 1Password continues to evolve on iOS we’ve been given new opportunities to take advantage of additions to the operating system that fulf

AgileBits 2.6k Jan 4, 2023