I am trying to get HKWorkout samples by HKSampleQuery in Apple's HealthKit. By the way I can't get HKWorkout's device name which was tracked by Apple Watch.
I could get several HKWorkout Sample data and confirm the sourceRevision and totalDistance are available. But the only device data couldn't be confirmed. It showed it has a null value.
Is Apple missing it on that?
Here is my code to experiment this.
func getRunningWorkouts(completionHandler: #escaping (_ data:[AnyObject]?, _ response:Bool, _ error:Error?) -> Void) {
let predicateForRunning = HKQuery.predicateForWorkouts(with: .running)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let sampleType = HKWorkoutType.workoutType()
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: predicateForRunning, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (query, resultsArray, error) in
if error != nil {
print("Get an error to extract running workout: \(error)")
return
}
if let samples = resultsArray as? [HKWorkout] {
for sample in samples {
print(sample.device?.name)
completionHandler(samples, true, nil)
}
}
}
}
healthStore.execute(sampleQuery)
}
You should file a bug with Apple if you believe that it's a mistake that the name of the Apple Watch is missing from the sample.
Related
I am working on an IOS sleep application where i need to do sleep analysis. I am using Healthkit for sleep data from where i can successfully fetch sleep analysis data using below code :
func retrieveSleepAnalysis(from startDate: Date?, to endDate: Date? , completion: #escaping ([HKCategorySample], Error?) -> Void) {
guard let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis) else { return}
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: 10000, sortDescriptors: [sortDescriptor]) { (query, result, error) in
if error != nil {
completion([], error)
return
}
if let result = result {
let samples = result.compactMap({ $0 as? HKCategorySample})
completion(samples, nil)
}
}
// finally, we execute our query
HKHealthStore().execute(query)
}
I am not able to find any healthKit code for Sleep REM cycles , Deep sleep , light sleep etc. Is it even possible to get this data from healthKit ? if Yes , How to do it? , if not with healthKit, How to do it in IOS Applications ?
You may find what you're looking for in this year's WWDC talk on "What's New In HealthKit"
Here's how you can declare a predicate for all samples:
// Predicate for all asleep samples (unspecified, core, deep, REM)
let allAsleepPredicate = HKCategoryValueSleepAnalysis.predicateForSamples(equalTo: .allAsleepValues)
And here's how you might use it to fetch all samples in a range:
let healthStore = HKHealthStore()
let sleepType = HKCategoryType(.sleepAnalysis)
let dateRangePredicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [dateRangePredicate, allAsleepPredicate])
let query = HKSampleQuery(sampleType: sleepType, predicate: predicate) { (query, result, error) in
// handle results
}
Hope that helps
I'm trying to build an app with swift which will extract the heart rate from iwatch and display it to the user along with it will play some music in the user's iphone. Pretty new to ios ,so im trying to figure out how to extract data from healthkit in iwatch and sync it with the mobile app. Do i need to build 2 apps , one for watch and phone or the same app? and how to integrate health kit since i have just enabled it in the target->capabilities.
public func subscribeToHeartBeatChanges() {
// Creating the sample for the heart rate
guard let sampleType: HKSampleType =
HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
}
/// Creating an observer, so updates are received whenever HealthKit’s
// heart rate data changes.
self.heartRateQuery = HKObserverQuery.init(
sampleType: sampleType,
predicate: nil) { [weak self] _, _, error in
guard error == nil else {
log.warn(error!)
return
}
/// When the completion is called, an other query is executed
/// to fetch the latest heart rate
self.fetchLatestHeartRateSample(completion: { sample in
guard let sample = sample else {
return
}
/// The completion in called on a background thread, but we
/// need to update the UI on the main.
DispatchQueue.main.async {
/// Converting the heart rate to bpm
let heartRateUnit = HKUnit(from: "count/min")
let heartRate = sample
.quantity
.doubleValue(for: heartRateUnit)
/// Updating the UI with the retrieved value
self?.heartRateLabel.setText("\(Int(heartRate))")
}
})
}
}
public func fetchLatestHeartRateSample(
completion: #escaping (_ sample: HKQuantitySample?) -> Void) {
/// Create sample type for the heart rate
guard let sampleType = HKObjectType
.quantityType(forIdentifier: .heartRate) else {
completion(nil)
return
}
/// Predicate for specifiying start and end dates for the query
let predicate = HKQuery
.predicateForSamples(
withStart: Date.distantPast,
end: Date(),
options: .strictEndDate)
/// Set sorting by date.
let sortDescriptor = NSSortDescriptor(
key: HKSampleSortIdentifierStartDate,
ascending: false)
/// Create the query
let query = HKSampleQuery(
sampleType: sampleType,
predicate: predicate,
limit: Int(HKObjectQueryNoLimit),
sortDescriptors: [sortDescriptor]) { (_, results, error) in
guard error == nil else {
print("Error: \(error!.localizedDescription)")
return
}
completion(results?[0] as? HKQuantitySample)
}
self.healthStore.execute(query)
}
let endDate = NSDate()
let startDate = NSDate()
let v : Float?
let stepsCount:HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!
let predicate:NSPredicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: .None)
let query = HKQuantitySample(sampleType: stepsCount, predicate: predicate, limit: 1, sortDescriptors: nil, resultsHandler: {
(query, results, error) in
if results == nil {
print(error)
}
v = result.first.Quantity
})
healthStore.executeQuery(query)
Cannot find an initializer for type 'HKQuantitySample' that accepts
an argument list of type '(sampleType: HKQuantityType, predicate:
NSPredicate, limit: Int, sortDescriptors: nil, resultsHandler: (_, _,
_) -> _)'
Just replace HKQuantitySample with HKSampleQuery and it will work fine.
For more Info refer THIS tutorial.
Where you can find sample code like:
func readMostRecentSample(sampleType:HKSampleType , completion: ((HKSample!, NSError!) -> Void)!)
{
// 1. Build the Predicate
let past = NSDate.distantPast() as! NSDate
let now = NSDate()
let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)
// 2. Build the sort descriptor to return the samples in descending order
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// 3. we want to limit the number of samples returned by the query to just 1 (the most recent)
let limit = 1
// 4. Build samples query
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
completion(nil,error)
return;
}
// Get the first sample
let mostRecentSample = results.first as? HKQuantitySample
// Execute the completion closure
if completion != nil {
completion(mostRecentSample,nil)
}
}
// 5. Execute the Query
self.healthKitStore.executeQuery(sampleQuery)
}
Documentation doesn't talk about any initializer like the one you are providing… Even looked in the Beta docs and didn't found anything about the one you are trying to call.
Please look here for existing HKQuantitySample initializers available :
https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKQuantitySample_Class/
See Dharmesh Kheni's answer for the correct way to create a query :).
I'm following some tutorials on HealthKit using swift, one of the tutorials I'm following is how to retrieve some data from the HealthKit such as weight, height age. The tutorial shows how to retrieve the most recent record for each them, the following codes shows that:
func readMostRecentSample(sampleType:HKSampleType , completion: ((HKSample!, NSError!) -> Void)!)
{
// 1. Build the Predicate
let past = NSDate.distantPast() as! NSDate
let now = NSDate()
let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)
// 2. Build the sort descriptor to return the samples in descending order
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// 3. we want to limit the number of samples returned by the query to just 1 (the most recent)
let limit = 1
// 4. Build samples query
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
completion(nil,error)
return;
}
// Get the first sample
let mostRecentSample = results.first as? HKQuantitySample
// Execute the completion closure
if completion != nil {
completion(mostRecentSample,nil)
}
}
// 5. Execute the Query
self.healthKitStore.executeQuery(sampleQuery)
}
Then in other class the developer passes the parameters to get the required most recent record, the following code shows the method that retrieves the height record:
func updateHeight()
{
// 1. Construct an HKSampleType for Height
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)
// 2. Call the method to read the most recent Height sample
self.healthManager?.readMostRecentSample(sampleType, completion: { (mostRecentHeight, error) -> Void in
if( error != nil )
{
println("Error reading height from HealthKit Store: \(error.localizedDescription)")
return;
}
var heightLocalizedString = self.kUnknownString;
self.height = mostRecentHeight as? HKQuantitySample;
// 3. Format the height to display it on the screen
if let meters = self.height?.quantity.doubleValueForUnit(HKUnit.meterUnit()) {
let heightFormatter = NSLengthFormatter()
heightFormatter.forPersonHeightUse = true;
heightLocalizedString = heightFormatter.stringFromMeters(meters);
}
// 4. Update UI. HealthKit use an internal queue. We make sure that we interact with the UI in the main thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.heightLabel.text = heightLocalizedString
self.updateBMI()
});
})
}
I created a similar method to the first one, but with few changes so I can get an array of 10 glucose records:
func readAllGlucose(sampleType:HKSampleType , completion: (([HKSample!], NSError!) -> Void)!)
{
let now = NSDate()
let df = NSDateFormatter()
df.dateFormat = "yyyy-MM-dd"
let pastt = df.dateFromString("2015-05-18")
//let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)
let allreadings = HKQuery.predicateForSamplesWithStartDate(pastt, endDate: now, options: .None)
// 2. Build the sort descriptor to return the samples in descending order
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// 3. we want to limit the number of samples returned by the query to just 1 (the most recent)
let limit = 10
// 4. Build samples query
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: allreadings, limit: limit, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
completion([nil],error)
return;
}
// Get the first sample
let allSamples = results as? [HKQuantitySample]
// Execute the completion closure
if completion != nil {
completion(allSamples!,nil)
}
}
// 5. Execute the Query
self.healthKitStore.executeQuery(sampleQuery)
}
Then I created another method similar to updateHeight() method,but of course with doing the necessary changes:
func updateLastGlucoRecords()
{
// 1. Construct an HKSampleType for weight
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)
// 2. Call the method to read the most recent weight sample
self.healthManager?.readAllGlucose(sampleType, completion: {([allReadings], error) -> Void in
if (error != nil) {
println("Error reading glucose readings from HealthKit Store: \(error.localizedDescription)")
return;
}
var glucoseLocalizedString = self.kUnknownString;
self.glucose = allReadings as? [HKQuantitySample]
for reading in readings {
if let record = reading.quantity {
glucoseLocalizedString = record
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.glucoReadings.append("\(glucoseLocalizedString)")
})
}
})
self.healthManager?.readMostRecentSample(sampleType, completion: { (mostRecentWeight, error) -> Void in
if( error != nil )
{
println("Error reading weight from HealthKit Store: \(error.localizedDescription)")
return;
}
var weightLocalizedString = self.kUnknownString;
// 3. Format the weight to display it on the screen
self.weight = mostRecentWeight as? HKQuantitySample;
if let kilograms = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) {
let weightFormatter = NSMassFormatter()
weightFormatter.forPersonMassUse = true;
weightLocalizedString = weightFormatter.stringFromKilograms(kilograms)
}
// 4. Update UI in the main thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.weightLabel.text = weightLocalizedString
self.updateBMI()
});
});
}
But unfortunately I'm getting two errors at the line:
self.healthManager?.readAllGlucose(sampleType, completion: {([allReadings], error) -> Void in
first error:
[HKSample!]' is not a subtype of '<>
second error:
Use of undeclared type 'allReadings'
I have another question, how can I get the metadata from each object, I want to get the date and time that the data was inserted
If someone have any idea that could solve this problem I will be thankful and grateful for that
thanks in advance
I was having the same problem . Please look into my code below .
1.) allReadings error is due to the reason that you have not passed the array of HKSample so to make it so you have to write as: let allReadings:[HKSample!] and then pass allReadings.
func readAllSample(sampleType:HKSampleType , completion: (([HKSample!], NSError!) -> Void)!)
{
let now = NSDate()
let past = now.dateByAddingTimeInterval(-10*24*60*60) as NSDate!
let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
let limit = 10
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if(error == nil)
{
print(results)
}
}
self.healthKitStore.executeQuery(sampleQuery)
}
I am using iHealth devices to get the vitals from device using there iHealth app,those data is stored in Health app. I have configured my application to communicate with Health app but I have no idea how to get the stored data of health to my own custom app.
As there are no examples for this issue and the documentation also didn't provide in-depth information about it.
As long as you (or the user) of your app has granted access to your app to read what iHealth has stored in the HealthKit database, you can query it with the HealthKit APIs.
// 1. these are the items we want to read
let healthKitTypesToRead = NSSet(array:[
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass),
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)
])
// 2. these are the items we want to write
let healthKitTypesToWrite = NSSet(array:[
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass),
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)
])
// 3. Request HealthKit authorization
healthKitStore!.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead) { (success, error) -> Void in
if( completion != nil )
{
if (success == true) {
self.initialized = true
}
completion(success:success,error:error)
}
}
Then you can query for the data:
// 4. Build the Predicate
let past = NSDate.distantPast() as NSDate
let now = NSDate()
let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)
// Build the sort descriptor to return the samples in descending order
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// we want to limit the number of samples returned by the query to just 1 (the most recent)
let limit = 1
// Build samples query
let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
completion(nil,error)
return;
}
// Get the first sample
let mostRecentSample = results.first as? HKQuantitySample
// Execute the completion closure
if completion != nil {
completion(mostRecentSample,nil)
}
}
// 5. Execute the Query
self.healthKitStore!.executeQuery(sampleQuery)
}
FYI, After enable Healthkit in your AppID.You have to authenticate require from health store.
let healthKitStore:HKHealthStore = HKHealthStore()
func authorizeHealthKit(completion: ((success:Bool, error:NSError!) -> Void)!)
{
// 1. Set the types you want to read from HK Store
var healthKitTypesToRead = self.dataTypesToRead() as! Set<NSObject>
// 2. Set the types you want to write to HK Store
var healthKitTypesToWrite = self.dataTypesToWrite() as! Set<NSObject>
// 3. If the store is not available (for instance, iPad) return an error and don't go on.
if !HKHealthStore.isHealthDataAvailable()
{
let error = NSError(domain: "com.domain.....", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit is not available in this Device"])
if( completion != nil )
{
completion(success:false, error:error)
}
return;
}
else
{
// 4. Request HealthKit authorization
healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes:healthKitTypesToRead, completion: { (success, error) -> Void in
if( completion != nil )
{
completion(success:success,error:error)
}
})
}
}
Useful link as below:
https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/index.html#//apple_ref/doc/uid/TP40014707
https://en.wikipedia.org/wiki/Health_%28application%29
http://www.raywenderlich.com/86336/ios-8-healthkit-swift-getting-started
https://developer.apple.com/videos/wwdc/2014/#203