HKHealthStore is the way you interact with HealthKit on a users device. It provides the necessary tools to gather permission from the user as well as provides ways to fetch characteristic data, save data, and delete data.
Today’s tutorial will be fairly brief. We will look at a few of the abilities that this class gives to you. Some of them we have already covered. If you want to know how to save an object to Apple Health, check out this tutorial, or this tutorial. If you want to read characteristic data from the health store, that post is for you.
HKAuthorizationStatus
When reading data from the health store, Apple does not tell us if the user accepted or declined to allow us access. If they denied access your app will either see no data for a particular type, or it will only see what the user has authorised the app to write.
HKAuthorizationStatus is the way we check if we have permission to save data in HealthKit.
func checkAuthorization() { let authorizationStatus = healthStore.authorizationStatus(for: HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!) if authorizationStatus == HKAuthorizationStatus.sharingAuthorized { print("Authorised") } else if authorizationStatus == HKAuthorizationStatus.sharingDenied { print("Denied") } else { print("Not determined") } }
Line 2 we store the authorisation status. We could just include the full request for authorisation status in the if statement, but it becomes unreadable because of wrapping around to the next line.
Line 3 we check if the authorizationStatus == HKAuthorizationStatus.sharingAuthorized. If we are authorised, we can save at this point.
We check on line 5 if permission was denied. If this result is true, then you might consider letting the user know that your app is unable to save data.
On line 7 we are checking any other status, which at the moment leaves notDetermined. If the user hasn’t been presented with an authorisation screen, or the user cancelled out and didn’t choose, then this is what will be called. Full details can be found here about HKAuthorizationStatus.
Before we move on, there are currently a few methods marked as beta which expand on this a little. One of them determines if the app needs to request authorisation, while the other provides a value to indicate that you need to request authorisation. We will cover these when iOS 12 launches.
.isHealthAvailable
This is a simple class function that returns a bool. If the device is too old for Apple Health, or perhaps the device is an iPad, you will get a false. But if all is good, you will get a true. You need to perform this check each time you use HealthKit, as seen in our setting up HealthKit tutorial.
delete
Not surprisingly, the delete method is the opposite of the save method. Just as you can save data to HealthKit, you can also delete data. There are two available methods for this. The first accepts a single HKObject, while the second takes an array of HKObjects should you want to delete more than 1 item at once. Please be aware that your app can only delete objects that it has saved to the health store. You cannot delete other data that your app was not responsible for saving. Also, if the user adjusts permissions, you may be prevented from issuing a delete on those objects that you initially created.
Here is how it works:
func createObject() { let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass) let bodyMass = HKQuantitySample.init(type: quantityType!, quantity: HKQuantity.init(unit: HKUnit.pound(), doubleValue: 210.2), start: Date(), end: Date()) healthStore.save(bodyMass) { (success, error) in if !success { print(error) } else { print("Succesfully saved") self.fetchAndDeleteBodyMass() } } } func fetchAndDeleteBodyMass() { let sampleType = HKSampleType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass) let query = HKSampleQuery.init(sampleType: sampleType!, predicate: nil, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { (query, results, error) in let objectToDelete = results?.last print(objectToDelete?.description) self.healthStore.delete(objectToDelete!, withCompletion: { (success, error) in if !success { print(error) } else { print("Succesfully Deleted") } }) } healthStore.execute(query) }
The first function creates a bodyMass and saves it. We hardcode 210.2 pounds as the weight, and set the date to now for beginning and end.
On line 5 we determine if there was an error. Assuming there isn’t, then line 8 prints a friendly successfully saved message to the console, and line 9 calls fetchAndDeleteBodyMass().
The fetchAndDeleteBodyMass method has been created to simply show you how you can use a fetched object, and then pass that in to be deleted. Of course we wouldn’t automatically delete what we just saved moments before.
Line 15 we create the sample type. We then create the query on line 16 and request that all bodyMass samples be returned.
On line 19 the results handler is called when the query finishes execution (triggered on line 30). Here I just tell it to fetch the last object in the results array. We then print the description of that. We then pass that in to healthStore.delete, and we instruct the health store to delete the object.
We then get a success message on line 26.
This is just a simple overview of the mechanics of creating, saving, fetching, and then deleting a single quantity sample in the health store. Please adapt it to how you need to work with.
A third delete option is actually available. This particular delete allows you to delete by predicate and sample type. Instead of searching and naming specifics, you might want to say “delete all bodyMass entries for today recorded by this app”. The deleteObjects(of:predicate:withCompletion:) is the method to use in these cases.
earliestPermittedSampleDate
This is an instance method that can be called on the health store. It simply lets you know what is the earliest date that you can query samples for. The documentation tells us that you cannot query or save samples before this provided date.
stop
We have used the execute function throughout the HealthKit tutorials. What we haven’t yet used is stop. If you have a long-running query that you no longer want to keep running, you can call stop on that HKQuery.
healthStore.stop(query)
preferredUnits
If your app is interested in knowing what units the user prefers, then call this method. It will return the preferred unit type for the type that you request it for. Remember that you might be more familiar with lbs if you are in the US, but for UK users, they might prefer KG or Stone. This function will return what the user has set:
let quantityType : Set = [HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)!] print(healthStore.preferredUnits(for: quantityType, completion: { (results, error) in print(results) }))
The results of this will show just 1 object (because we only requested preferred type for bodyMass). The console will have a message printed something like this:
[HKQuantityTypeIdentifierBodyMass: lb]
Apple also suggests that you should subscribe to the HKUserPreferencesDidChange notification which will inform your app if the user makes a change in Apple Health. This is important because you might chart body mass based on KG, but then the user might switch to pounds. When this happens, your UI might need updating. Observing this notification would be the way your app can automatically respond to changes.
There’s More
There are a few more methods that we haven’t touched upon yet. In particular, we haven’t looked too much at the background delivery methods. Likewise, we haven’t looked at the workouts functions available. This is something we will cover when we create the workouts tutorial.
Leave a Reply
You must be logged in to post a comment.