In our last tutorial we looked at how to save quantity samples with HealthKit. Today we will look at how to save category samples. Just so you are aware of the difference, quantity samples contain a numeric value such as your weight, number of steps taken between 2 points of time, amongst many other things. A category sample is a type where information can be represented from a short list of options such as tracking your sleep. You are either asleep or you are not.
If we look at the sleep analysis category we can see in the documentation that there are 3 options. You are in bed, you are asleep, or you are awake. It is up to you, the developer, to decide how frequent the samples are recorded. An explanation of how to record category samples for sleep is found in the documentation.
In short, out of the 3 states you would record the time that the user was in bed, and then create a second sample with its own timings to show when they were asleep or not. The drawing below shows that the user was in bed for a few hours, slept for some of that, then got out of bed, and then got back in to bed, and then fell asleep, woke up, stayed in bed, fell back asleep, woke up, and then got out of bed. In bed shouldn’t be recorded as the opposite of sleep (ie, in bed is in bed, and not awake).
Category Sample Tutorial
In this tutorial we will save sleep data to the health store. This will demonstrate how to create the category sample and save it in the correct way. What we won’t be doing is actually tracking real sleep. Although that would make for an awesome tutorial; which I might create later; it goes beyond the scope of HealthKit and saving a category sample. Things to consider if tracking sleep in a tutorial are how to do just that. Do we use accelerometer data, watch data, phone data, phone on charge data, phone being moved data, etc… There are so many sources of data that your phone and watch can provide that it would take this HealthKit tutorial off track.
How to Save a Category Sample
Not surprisingly, saving a category sample is very similar to the way we save a quantity sample. There are a few minor differences, but the pattern discussed in yesterdays tutorial remains almost the same.
To begin, download the starter project here.
Lets begin by adding the request to write data to requestAuthorisation.
let writeDataTypes : Set = [HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!] healthStore.requestAuthorization(toShare: writeDataTypes, read: readDataTypes) { (success, error) in
Line 1 needs to be added to the view controller in the sample project, just above the healthStore.requestAuthorization line. On this line we are specifying that we want to be able to write sleep data to the healthStore.
Line 3 replaces what is on line 38 in the sample project. Here, we change the toShare: from nil to our newly created Set called writeDataTypes.
If you go ahead and run the app now, you should be asked for permission to write sleep data.
We next need to create a function where we will save the sleep data. To keep it simple we will just set it to record that you were in bed 8 hours prior to running the app, and that you fell asleep 10 minutes later, and then you woke up 10 minutes from the time you ran the app, and got out of bed just now.
Lets create a function called recordSleep().
func recordSleep() { }
We will call this on line 42 of the sample project like so:
self.recordSleep()
When the app launches it will check if permission has been requested. If not, then it will ask the user for permission. When done, the recordSleep function will be called. This is where you would typically pass in a start date, end date, and the state which you are recording. In the tutorial, we will just hardcode the data.
Now that the structure of the project is in place, we can now start to add code. We do this as follows:
func recordSleep() { let now = Date() let startBed = Calendar.current.date(byAdding: .hour, value: -8, to: now) let startSleep = Calendar.current.date(byAdding: .minute, value: -470, to: now) let endSleep = Calendar.current.date(byAdding: .minute, value: -5, to: now) let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) let inBed = HKCategorySample.init(type: sleepType!, value: HKCategoryValueSleepAnalysis.inBed.rawValue, start: startBed!, end: now) let asleep = HKCategorySample.init(type: sleepType!, value: HKCategoryValueSleepAnalysis.asleep.rawValue, start: startSleep!, end: endSleep!) healthStore.save([inBed, asleep]) { (success, error) in if !success { // Handle the error here. } else { print("Saved") } } }
In the sample code above, we do the following:
Lines 2-5 create some dates that we are manually creating to set times. Line 2 sets the date to now. Line 3 is where we -8 hours from the current time. This will be when we are “inBed”. Start sleep is 470 minutes prior to now, which is 10 minutes after getting in to bed. End sleep is where we wake up (which is 5 minutes before the app runs). Just to make sure you are clear on this; we would not typically feed dates in to the category sample in this way. Normally you would detect the time when the user went to bed, and when they were awake, and when they were asleep. You would then use those dates to feed in to the category sample initialiser.
Line 7 is where we define that we want to use a sleepAnalysis.
Line 8 is where we create our category sample of when the user is in bed. We provide startBed and now date for this (ie, we wanted to manually say we went to bed 8 hours ago and just got out of bed.
Line 9 is where we create the asleep sample. Again, we provide a start and end date. Note that there would likely be multiple sleep sessions for a nights sleep, but in this example we are pretending that we slept straight through.
Line 10 is where we save. Instead of passing in the inBed and asleep constants individually, save allows us to pass in single or an array of objects to save. We opt for the latter here and pass them both in as an array.
If the save is successful, you will get a message in the console saying saved. You will then be able to look at the data saved in Apple Health.
What to Watch Out For
Just as previous tutorials, to get rid of sample data you saved with this app, you can delete the app. You are then given the option to delete all saved samples that the app saved. Alternatively, you can just manually delete them from Apple Health.
Also, I hope it is clear that you would not manually create the dates in this way. This tutorial merely shows you the code of how to save inBed and asleep times. If you wanted, you could throw in some awake time as well alternating with when you are sleeping.
Most sleep apps that I have used utilise sensors to detect movement of the watch. You would typically analyse that data and then determine with that data what dates to pass in to the sleepAnalysis. Mixed with that you might also consider using the time the phone wasn’t touched, and the time it was on charge, as well as the time the alarm was set for. There are many ways to detect when someone might be sleeping, so if you are wanting to save this data, make use of all the sensors and other options in iOS to try create an accurate picture of when the user is actually asleep.
Sample Code Download
You can download the full project from here. If you have any questions or ideas, please use the discourse link below and ask away on the discussion board.
Leave a Reply
You must be logged in to post a comment.