Build feature-rich research and care apps with CareKit: Learn about the latest advancements to our health framework, including new views for its modular architecture, improvements to the data store, and tighter integration with other frameworks on iOS. And discover how the open-source community continues to leverage CareKit to allow developers to push the boundaries of digital health — all while preserving privacy.
Hello, Hello! My name is Gavi Rawson and I'm a software engineer on the Apple Health team working on the CareKit framework. Later we'll be joined by my colleague Eric Hornberger who works on the framework as well. We're here to talk to you today about the updates that we've added to CareKit this year. So what is CareKit? CareKit is an open source framework that helps you build beautiful care apps. The framework is split up into three parts.
We have CareKit, CareKitUI, and CareKit Store. Each part is a framework in and of itself and can be imported using Swift Package Manager. CareKit Store provides health flavored data models and a core data layer for persistence.
CareKitUI provides static views that are perfect for displaying that data and CareKit ties the UI and store layers together by providing synchronization between the two. When data in the store changes, the views are automatically updated.
Last year, we re-architected the framework using Swift. We made sure to design the framework in such a way that it's easy to use for beginners but, also provides enough customization hooks for more advanced developers.
This year we focused on strengthening the framework and building out even more new tools that make it easier for you to create your care Apps. Instead of giving you a boring, old agenda, we've created this sticker pack for all the new CareKit features, because who doesn't love stickers? By the end of the talk today we'll collect all the stickers and tack them onto our MacBook. So, let's kick things off today in CareKit UI which provides different types of views for visualizing tasks, charts, and contacts. This year we're adding even more new views to the framework. Let's look at these new views in the context of a wellness application built using CareKit.
Here we have my personal feed showing a list of cards relevant to me. Each card here is a new view that we've added to the framework. The first is the SimpleTaskView reminding me to do my daily stretches. If you've worked with CareKit before this view might look familiar to you. We already have a UIKit API for the SimpleTaskView and now we've added a SwiftUI API as well. Let's look at that SwiftUI API in detail. We start by importing CareKitUI and SwiftUI and we write some boilerplate SwiftUI code. Then we add the SimpleTaskView to the body. We initialize the view with a title, a detail, and a flag that determines if the task is complete or not.
But this view is just a starting point. So, let's say you want to customize it just a bit. We'll take a closer look at the title and detail parameters that accept SwiftUI text rather than a string. This allows you to decorate the title in detail using custom view modifiers. Notice how we changed the title to a thin font weight to provide less emphasis in the header.
We'll jump back to our default font weight and check out another customization point available to us. SwiftUI encourages you to build small view components then compose them together to create more rich and functional views. This simple task view is built from small view components as well. We have a detailed disclosure and a header. If you'd like, you have the option of injecting your own custom header or detailed disclosure into the view in place of the default ones we provide for you. Let's try creating a custom header for this view. We'll go back to our code and use another initializer available to us. This one provides us a closure where we can build our custom header and CareKitUI provides a few small view components to help us along the way. In particular we can use a header view to help us match the style of other CareKitUI cards. We pass a title and a detail to the header and place it beside a custom accent bar in an HStack. With that the view is already starting to look custom, but let's go even deeper using another customization point. We can attach custom views to any side of the content here, which allows us to extend the card in any direction. Let's look at some code to attach content to the bottom of the view. We'll start by creating a new view just so that we have more space to work. Then we wrap our SimpleTaskView inside of a CardView and place it beside a divider and instructions text. The CardView is another component from CareKitUI and when wrapping one card inside of another, only the outermost card will be displayed. This allows us to put all of our content into a single card.
And with that we're all done building our custom view. And you can see it looks very different from when we started. We started with the simple task view that had just a header and a completion button. And now we have a custom accent bar and detailed instructions for the task. Let's go back to our wellness application and look at some of the other new views in CareKitUI. The next new view is the LabeledValueView. This one is great for displaying a value and its associated units. Here we're showing my heart rate measurement is 62 beats per minute which seems a bit generous.
We can create this view by providing title and detailed text just like in the previous view. We also provide the completion state. If you'd like to customize the view even further, we provide identical hooks into the view as the ones we saw for the SimpleTaskView. The next new addition to CareKitUI is the NumericProgress- TaskView which is helpful for displaying my cumulative progress towards a goal. Here you can see that I've exercised for twenty two minutes in pursuit of my 30 minute goal. Just a gentle reminder to get back out there. To create the view we again pass a title and a detail to display in the header. We also provide instructions for the task and the text for the progress and goal. Lastly we pass on a flag that determines if the task is complete. The next new view is the FeaturedContentView and this one is great for highlighting important information such as an article for me to read. Here we have a great article on easy and healthy recipes that I can cook at home. Creating that Featured Content view is a bit different from before. We start by importing CareKitUI and UIKit.
We then define the view. Then set the large image in the background and the text on the bottom of the view. When the FeaturedContentView is tapped, we can display a new detail view. The detail view supports HTML and CSS in the content under the image, allowing you to put virtually anything in here. Creating the view is similar to the last one. We start by importing CareKitUI and UIKit. Then we define styled HTML which is a combination of HTML and the associated CSS for styling. We then instantiate the view by passing in the style HTML and a flag that determines whether or not to show the closed button in the top right corner.
Finally, we set the image just like we did before. The last new view is the LinkView which is great for displaying buttons that present new views directly inside or outside of the application. Here I have links to help me schedule a physical therapist appointment. We create the view by supplying a title, instructions, and the links to display. Here we create a link button that opens up a Web site inside of the app. These link options are just a few of the many that we provide. We also provide options like navigating to the app store. But if we don't address your use case there's an option to provide a custom URL as well. So, we've reached the end of today's feed in the app and that finishes up with all of the new views that we're adding to the framework. These are just a few new views, but we're essentially laying a roadmap for a UIKit and SwiftUI APIs. We now look to all of you in the community to build on top of what we have and improve the framework with new views for new use cases. With that we can get our first sticker for CareKitUI views and we can tack that on right next to our "dub dub" sticker.
So we just saw how we can create static views Using CareKitUI but CareKit goes one step further and wraps the static views in a synchronization layer so that when data in the store is updated, the UI will reflect the changes.
So now that we have new SwiftUI views in CareKitUI, we are adding synchronized swift UI views in CareKit. Let's look at how we can create one of these views.
We start by importing CareKit, CareKitUI, and SwiftUI. Then the body. We can create the simple task view from CareKit by providing a taskID an eventQuery and a synchronized store- Manager that holds a reference to the CareKit store. The View will use the taskID and the eventQuery to locate the task data in that store. Once it's located it'll be automatically mapped to the view and, after that, the synchronized store manager will ensure that the view is updated when task data changes. But you may want to customize the way that the task data is mapped to the view. To do that we can use another initializer. In this one we provide the same parameters as before but we also provide a closure where we can create the underlying view from CareKitUI to display. The closure will be called each time swift UI recomputes the body of this view. Inside of the closure we have access to a controller that holds a reference to the task data and a view model. The view model is a convenient construct to help us instantiate the underlying view. So let's use that view model to instantiate the simple task view from CareKitUI.
Since we're creating the view from CareKitUI we have access to all of the customization points that we saw earlier for CareKitUI views.
So, with the help of the view model we've essentially created the default simple task view. To make things a little bit more interesting, let's try modifying this view to display a ResearchKit survey when it's tapped.
To do that we first add a state property that determines whether or not the survey is showing. Then we change the DisplayTask to the ResearchKit- SurveyTask. After that we can modify the action to set the isShowingSurvey flag to "true" when the view is tapped. And, finally, when that flag is true we can present a popover with the ResearchKitSurvey. We've seen a lot of you out there using CareKit Cards to present ResearchKitSurveys, so this should be a really good starting point for you all. All right! That finishes up our section. Let's get our new sticker for synchronized views and build up our collection even more. We've clearly embraced Swift UI and our API and it's brought many advantages to the framework including a simplified API and many customization points. But one of the biggest advantages is that Swift UI has allowed us to bring CareKit to the Apple Watch.
You can now build CareKit, CareKit UI and CareKit Store all for watchOS.
On the UI side we currently support the Simple and Instructions task views and each have been fine tuned for the Apple Watch screen. Like we said before, these are just a few new views and we're super excited to see you all take them one step further and build new views for the Apple Watch. That finishes up our really short section, so let's get a shiny new watch sticker.
Now let's move on to some updates to the CareKit Store which is perfect for storing health data in your app. But while the CareKits Store is useful we already have a store on our devices today that's storing an immense amount of health data. That store is HealthKit. And now you can use data in HealthKit alongside CareKit data to create health kit driven tasks.
The tasks can be stored in a CareKit Store and can be auto-completed based on data that comes out of HealthKit. Before diving into the new HealthKit architecture let's look at the current architecture around the CareKit Store.
We have an OCKStore which is a core data implementation. The store can be wrapped by a StoreManager that powers synchronization in the views by omitting notifications when data in the store changes. The StoreManager can be used to create synchronized views in CareKit like the ones we saw earlier. Now let's look at how HealthKit integration fits into the picture.
We've created a new HealthKit passthrough store that sits beside the CareKit Store. While the CareKit Store uses core data as its source of truth, the HealthKit passthrough store uses HealthKit as its source of truth. The two stores can be wrapped with a new StoreCoordinator. We can interact with the StoreCoordinator in the same way that we interact with the individual stores by calling create, update, and delete methods for CareKit entities.
So let's dive deeper into the Store- Coordinator to see how it communicates with its internal stores. When we ask the store coordinator to fetch data it aggregates results from its internal stores. But if we ask the StoreCoordinator to write data it only writes to a single store at a time which helps ensure that writing data is a transactional operation.
Now let's take a look at some code that actually sets up this new HealthKit integration.
We'll start by importing CareKit and CareKit Store. Next create both the CareKit Store and the HealthKit- PassThroughstore making sure to give each a name that's unique for your application. Then create a StoreCoordinator and attach the two stores that we've just created. Finally, create a store- Manager using the store coordinator. And this StoreManager can now be used to create synchronized CareKit views. So now that we have a store set up let's actually create an exercise task to add to it. We create a schedule for the task which specifies that the task occurs at 8AM every day.
It also sets a target value of 30 exercise minutes which will be used by the views to display the exercise goal. Next, we create a HealthKitLinkage to help us link the task to a HealthKit quantity. We can use the quantity- Identifier, type, and unit that correspond to the HealthKit datatype. Now that we have a schedule and a HealthKit- Linkage, we can create the new task and add it to the store. To display the task we can use some of the new views that we've introduced in CareKitUI. In this case the numeric progress task view is a perfect fit because it shows a progress and a goal value.
The labeled value task view above it is better for displaying tasks that don't have a particular goal. All right. That finishes up another section.
We already have a lot of stickers but let's make room for one more for HealthKit driven tasks. In addition to HealthKit there are many other storage systems out there today in the health care industry. The vast majority of them store their data in a format called FHIR and FHIR is used so that data can be easily exchanged and pars.ed And to make it easier for you to interact with these databases in your apps, we're introducing FHIR compatibility.
To understand FHIR a little bit better, let's look at a snippet of JSON that structured in the FHIR format. This JSON represents data for a medication order of Tylenol. The JSON is formed in a certain release of FHIR. In CareKit, We support compatibility with a few releases including DSTU 2 and R4.
To enable FHIR compatibility we're introducing coders that can map between CareKit entities and FHIR data. To accomplish this mapping we make use of a new open source Apple framework called FHIRModels. For more information on the framework, check out the session, Handling FHIR Without Getting Burned, which is one of the best named "dub dub" sessions out there. When we're doing this mapping of FHIR data to chemical entities, oftentimes a single FHIR resource maps to a single CareKit entity. A good example is a FHIR patient.
But sometimes FHIR resources are more granular than CareKit entities.
And in those cases a few FHIR resources might map to a single CareKit entity.
It's important to take this into account when doing the mapping yourselves.
So now that we understand the coders at a high level, let's actually create them.
We'll start by importing CareKit Store and CareKitFHIR which is a new SPM package that contains the coders. We can then initialize the coder that's responsible for mapping the data. And here we have a coder that will map JSON in the R4 release to CareKit entities. Using that coder we can take a CareKit patient and convert it to FHIR data. In the reverse direction we can take FHIR data and map it to a CareKit patient.
Notice that in the process we first create a FHIRResourceData. This helps ensure that the binary data is JSON in the R4 format so that it can be safely passed to our R4 coder. But the mapping between FHIR data and CareKit entities isn't always so perfect. For example it's possible that a property on a CareKit entity cannot be perfectly represented by a FHIR resource. In those cases it's important to define the mapping yourself so that data isn't lost in translation. Here you can define the way that a patient name is mapped to the FHIR data. In this closure were given a name and a patient from the FHIR models framework. Our job is to actually map the name to the patient. In the reverse direction, you can define the way that a patient name is mapped from the FHIR data. Here we're given a patient from the FHIR models framework, but this time our job is to extract the name components for the CareKit entity. Notice that these closures exposed types from FHIR models. Like we mentioned before, the framework is completely open source. And you can find all of the source code on the CareKit GitHub page. That finishes up the FHIR updates. Let's see what our new sticker looks like. Awesome! And now I'll pass it off to my colleague, Eric, who's going to go through some more exciting updates to the CareKit Store. Onto you Eric! Thanks Gavi. And hello everybody. My name's Eric. I'm also an engineer on the CareKit team. And today I have the privilege of introducing and demoing an exciting new feature and CareKit. Gavi's just talked to you a bit about how CareKit helps keep your views in sync with the data in your store. What I'm going to talk with you about is a different kind of synchronization. Synchronization with a server. One of the most common questions we've received since CareKit was open sourced has been "How do I synchronize the data in my CareKit App with a server?" We've put a lot of time and thought into considering how to do that and how to do it well. And today I'm happy to introduce a new set of CareKit Remote Synchronization APIs. The new Remote Sync API defines a protocol for communicating with CareKit.
Any server that observes these rules can be used as a synchronization back end for CareKit Apps. When a CareKit App enables remote synchronization, changes made locally such as completing a task will be synchronized to a remote server. Other devices can interact with the data on the server as well. Here are the other device adds a new task for this user. The next time our patient synchronizes with the server, the store receives the updates and it can be displayed to them. There are two sides to the new APIs that enable these interactions. The first lives on an extension to OCKStore and is for iOS developers. The second is a new protocol for adding support for CareKit to servers. Let's start with the App developer facing bits of the API. We've added a new remote parameter to OCKStore's initializer.
If you pass an argument into this parameter, CareKit will enable remote synchronization with the object you provided. If you leave the remote nil, then CareKit will continue to function entirely offline as it did previously.
We've also added a new synchronized method and, as its name suggests, calling this method will prompt CareKit to synchronize its local store with its remote store. The policy parameters default value is generally the one that you want. But there are other options that allow you to completely overwrite the data on either the local or the remote with the data from its partner. Let's take a look at how you use these in practice. First, you'll need to import CareKit Store plus a vendor SDK. We'll talk more about these packages in a moment but let's assume that we have one for now.
The next step is to instantiate a class that conforms to OCKRemoteSynchronizable.
You'll need to pass that instance into OCKStore's initializer. And once you've done that, just use the store as you normally would. Many remotes support automatic synchronization which means that CareKit will call the synchronized method for you as needed. Of course you're always welcome to call synchronized manually to kick it off on your own. So we've just had a look at the app developer facing side of the API. The other side of the coin is the new OCKRemoteSynchronizable Protocol that backend engineers and cloud providers can use to add support for CareKit to their servers.
This protocol has five requirements. You'll need to equip your class with a delegate and it'll be your responsibility to alert the delegate when changes happen on the server that CareKit needs to be aware of. You'll also need to tell CareKit if you want automatic synchronization or not.
It can be helpful to turn this off for deterministic unit tests but, typically, you'll want this to be "true". You'll also need to provide a method for fetching changes from the server. CareKit will give you a knowledge vector, also known as a vector clock in other contexts. This is a specialized data structure that allows the server to know exactly what data already exists on your device and what doesn't. Your job will be to send this vector to the server, exchange it for a revision record, pass the revision record into the merge revision closure, and indicate when you're done by calling in the completion block.
The next requirement is pushRevisions. CareKit will provide you with a device or vision which documents all of the changes that have happened on the device since the last time it checked in with the server. It's your job to pass this revision record to your server and tell CareKit when you're done by calling the completion closure. Finally, chooseConflictResolutionPolicy will be invoked when conflicting edits are made on the device and on the server. The conflict description contains a copy of both versions of the conflicted entity and your job will be to inspect both and decide which one to keep. By implementing this protocol plus the server side logic to support it it's possible to create new integrations with CareKit. Now, Apple does not provide a bespoke server implementation, but we have made it as easy as we can for others to do so. And we're thrilled to announce that we already have one partner lined up. IBM has become the very first to add support for CareKit to the IBM Cloud Hyper Protect offering. In the spirit of CareKit, IBM has even open sourced their work as part of the CareKit organization on GitHub. If you'd like to learn more about their SDK you can follow their self-guided lab to find out how to get started. All right. So we've done a lot of talking now and I'm really excited to get to the showing. Today we're going to go through a demonstration that uses the new Sync API. We've just introduced to do something that we think is pretty clever. Now the RemoteSync API is primarily intended for synchronizing and iOS CareKit App with a server. But I'm going to show you how we can use the very same API to synchronize data between an IOS app and its companion watchOS app. The app we're going to be creating today will remind our user to stretch daily, prompt them to report any muscle cramps, and show them a chart to illustrate the relationship between the two. Our goal is to get to a point where the same tasks are displayed on both devices and completing them on one device should automatically update them on the other.
The iOS application is actually just a pruned down version of the very same sample app that we built out in our talk from last year. If you haven't seen that presentation yet I'd highly recommend going back and watching it learn how we got to where we're going to be jumping in today. All right let's hop into Xcode.
As I mentioned a moment ago, the iOS application is actually complete already.
That means that we'll be able to focus on creating a stellar watchOS experience.
Let's begin by setting up the synchronization between our Apple Watch app and our iOS app.
We'll do that by leveraging a new class The OCKWatchConnectivityPeer.
This class conforms to the remote synchronizable protocol. And it makes it possible for the Apple Watch and iPhone to act as remote stores for one another. We'll need to pass this remote into the store when we instantiate it.
Now in today's demo we're going to be working with WatchConnectivity.
And when using WatchConnectivity, all of the messages passed back and forth between iOS and watchOS get funneled through the WatchConnectivity session delegate which is a class that's typically owned and controlled by you, the App developer. What that means is that CareKit is going to require a little bit of cooperation on your part in order to get its messages back and forth. I'll walk you through what we need to do.
We'll need to setup and activate our WatchConnectivity session. That'll entail setting the delegate and calling the activate method. For our purposes we're going to define a little helper class to function as our delegate.
The watch connectivity sessionDelegate has two required methods. The first, activationDidComplete seems like the perfect place to kick off our very first synchronization. The second method, didReceiveMessage will be triggered each time a message from the iPhone lands on the Apple Watch. And this is where we're going to give CareKitT a bit of help. Each time we receive a message we're going to show that message to our CareKit remote and give it an opportunity to furnish a response. We'll then take that response and forward it back to the iPhone on CareKits behalf. Now that that's out of the way, we'll just need to create an instance of our new class and remember to set it as the session delegate.
Now typically we would need to perform this exact same setup on the iOS side as well. But because it really is the exact same setup. We've gone ahead and done some demo magic and we've taken care of that already.
We have our remote setup on watchOS. We have our remote setup on iOS and our synchronization story is complete. We're going to move on and tackle the views next. In order to keep the views up to date with the latest data in the store we'll need to provide them with a reference to a synchronized StoreManager. Let's create one right here. We'll be building out our views in SwiftUI today. And when using SwiftUI, environment values can be a great way to provide a reference to your StoreManager to your views. Here we're defining a new environment key that provides a synchronized Store- Manager for the default value. Let's use the instance owned by our extension delegate.
We'll also extend environment values to define a new property, StoreManager.
And we'll use this property as an environment variable inside of our Todays- TasksView. Now that we have a reference to the store manager we can begin fleshing out our view. We want to display two task cards and we'll wrap both of them inside of a scroll view. We'll also go ahead and tint that scroll view red to match the theme of our iOS application. Now on iOS we chose to use the InstructionsTaskView for the stretch task. So we'll do the exact same thing here. Similarly, we'll use the simple task view for the cramps card. And because reporting cramps is sufficiently self-explanatory we'll demonstrate the use of a secondary initializer to hide away the detail label and create a nice crisp view. With our view complete our app is now finished.
Let's build, run and see how it looks. Note that the very first time we run our app we won't see any tasks appear immediately on the Apple Watch.
This is because it will take a moment for synchronization between watchOS and iOS to complete. But once it does we should see our tasks appear on the Apple Watch. And we do! Let's go ahead and try reporting muscle cramps on the iPhone to see if it makes it across to our Apple Watch. It does! This is looking really good! Remember, all of the views in CareKit are synchronized with the store.
What that means is that, in a moment, when I check off our stretch task on the Apple Watch, we should see all the subscribed views on the iPhone update simultaneously.
Keep an eye on the adherence ring, the stretch task card, and the chart as I complete our task. There is just something about that that is really satisfying! There's a bit more here that we could tinker around with but I think that you all get the gist. So, I'm gonna go ahead and wind down our demo right here.
Let's recap. We've taken a look at the new APIs in CareKit. We've also looked at how you can add support for CareKit to existing servers or clouds.
Then we demonstrated how these new APIs can be used to synchronize an Apple Watch with an iPhone. We've worked really hard for this one so let's claim our remote synchronization sticker. I'm going to hand things back to Gavi now and he's going to cover our very last topic for today; community updates.
Take it away, Gavi. Thanks, Eric. We're really excited about the new remote synchronization API and we can't wait to see how all of you will use it in your Apps. Like Eric mentioned, next we'll go through some community updates. And each year there's amazing work that's done by all of you and the community. And I'm super excited to share those updates with you today. Eric just talked all about our new remote synchronization API. As a member of our community IBM has been one of the first adopters of the API. You can find all of their source code on the CareKit GitHub page. Setting remote synchronization aside each year we love to highlight amazing Apps that are on the App Store today making great use of CareKit and ResearchKit. To call out a few, the OYM Athlete App helps support athletes nutrition goals by providing daily meal tracking and trends using our chart views. The Health Connected App makes it easy for you to share health data with your doctors and family members and makes use of contact views and charts as well. We've also seen Apps leverage CareKit and ResearchKit to quickly respond to COVID-19.
The Stanford First Responder App is helping responders assess their risk for COIVD-19 and quickly get them tested. The University of Nebraska's 1-Check COVID App is providing investigators with greater awareness of the disease.
These are just a few of the apps released this year, but we'd love to hear how you've been leveraging the frameworks. And we'll show you how to do that in just a bit. Last year we announced that we'd be releasing a brand new website. It's out there now at ResearchAndCare.org and acts as a beautiful landing page for the frameworks. On our overview page, you can get a snapshot of the features in both CareKit and ResearchKit and get insight into how the framework's can help you in your app. To get more specific information on CareKit you can navigate to the CareKit tab where you can browse through CareKit features and, as you scroll more, you can read case studies from programs that are using the frameworks to help people on a daily basis. You can also check out our Investigator Support Program which aims to support researchers with Apple watches for their studies.
And if you're interested in applying for this program, please reach out to us using the email address on this page. And finally there are a lot of you out there using ResearchKit and CareKit in your Apps. Like I mentioned before, we'd love to hear all about your use case and how you've been leveraging the frameworks. So please reach out to us using this submission form. Now comes a bittersweet moment where we get our last new ticker for Community Updates.
It's been a long journey and we've talked about everything from a beautiful new UI to enhancements to the CareKit Store. I hope you're all as excited as I am about all of the new updates. I think it's about time we earned our badge for this year's new CareKit features. To get started with the framework, make sure to check out our GitHub page and newly designed Web site. And as you all know, we're an open source framework and we're made better by all of you out there in the community that put in the hard work to build new features and contribute them back. So don't be afraid to open up your first PR on GitHub, whether it's big or small. And to finish things off today the team just wants to take a moment to thank all of you for being amazing supporters of the framework and for improving health in the world.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.