I am trying to create a label which displays today's steps from HealthKit. I have got as far as allowing HealthKit to read/write data, but I'm stuck on actually obtaining the data.
Can anyone offer any advice? Here's my code, including the label I want to display the steps:
import Foundation
import UIKit
import HealthKit
class HealthKitPage : UIViewController
{
let healthStore: HKHealthStore = HKHealthStore()
override func viewDidLoad()
{
var shareTypes = Set<HKSampleType>()
shareTypes.insert(HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!)
var readTypes = Set<HKObjectType>()
readTypes.insert(HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!)
healthStore.requestAuthorization(toShare: shareTypes, read: readTypes) { (success, error) -> Void in
if success {
print("success")
} else {
print("failure")
}
if let error = error { print(error) }
}
}
#IBOutlet weak var StepCount: UILabel!
}
You can fetch today's steps using HKStatisticsQuery. You can now in your view controller use getTodaysSteps method and set your label's text property.
Swift 3.1:
let healthStore = HKHealthStore()
func getTodaysSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let startOfDay = Calendar.current.startOfDay(for: now)
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: now, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { (_, result, error) in
var resultCount = 0.0
guard let result = result else {
log.error("Failed to fetch steps = \(error?.localizedDescription ?? "N/A")")
completion(resultCount)
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: HKUnit.count())
}
DispatchQueue.main.async {
completion(resultCount)
}
}
healthStore.execute(query)
}
You can call this method like this:
override func viewDidLoad() {
super.viewDidLoad()
getTodaysStepCount { [weak self] steps in
self?.StepCount.text = "\(steps)"
}
}
Related
I'm trying to get Number of steps taken by user today using health kit
this is my code
import UIKit
import HealthKit
class ViewController: UIViewController {
let healthStore = HKHealthStore()
let alarmTime = Date().dayBefore
let endTime = Date()
override func viewDidLoad() {
super.viewDidLoad()
let obj = HealthkitManager()
let access = obj.authorisationHealthkit()
if(access){
getTodaysSteps { results in
print("no. of steps taken today \(results)")
}}
}
func getTodaysSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let startOfDay = Calendar.current.startOfDay(for: now)
print(startOfDay)
print(now)
let predicate = HKQuery.predicateForSamples(
withStart: startOfDay,
end: now,
options: .strictEndDate
)
let query1 = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate) { query, statistics, error in
DispatchQueue.main.async {
if let error = error {
print(error)
print(error.localizedDescription)
}
if let sum = statistics?.sumQuantity(){
print(statistics)
completion(sum.doubleValue(for: HKUnit.count()))
}
}
}
self.healthStore.execute(query1)
}
I'm getting
Access granted Error Domain=com.apple.healthkit Code=11 "No data
available for the specified predicate."
UserInfo={NSLocalizedDescription=No data available for the specified
predicate.}
error.
so Basically how to get today's steps taken by user?
why i am getting this error?
I'm trying to build an iPhone app based on the user's live heart rate data.
I was able to get the latest heart rate from the Health App but once new information is recorded the app is not updated until relaunch.
Is there a way to get heart rate data in live? Maybe with HKObserverQuery?
here's my code so far: (Who manages to pull the last heartbeat)
import Foundation
import UIKit
import HealthKit
class HealthStore {
var healthStore: HKHealthStore?
init() {
if HKHealthStore.isHealthDataAvailable(){
healthStore = HKHealthStore()
}
}
func requestAuthorization(completion: #escaping (Bool) -> Void){
let heartBeat = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
guard let healthStore = self.healthStore else {return completion(false)}
healthStore.requestAuthorization(toShare: [], read: [heartBeat]) { (success, error) in completion(success)
}
}
func latestHarteRate(){
guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
}
let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictEndDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: [sortDescriptor]){(sample,result,error) in guard error == nil else{
return
}
let data = result![0] as! HKQuantitySample
let unit = HKUnit(from: "count/min")
let latestHr = data.quantity.doubleValue(for: unit)
print("Latest Hr\(latestHr) BPM")
healthStore?.execute(query)
}
You can use an HKAnchoredObjectQuery to create a query that returns an initial set of data and then updates to the data set.
Unfortunately, you can't provide a sort descriptor to the HKAnchoredObjectQuery, so you need to sort the data after you receive it if you don't want ascending order.
Here is a model object I created so that I could test in SwiftUI.
It creates an HKAnchoredQuery and sets an update handler function. The update handler converts the HealthKit results into my HeartRateEntry struct (This is so I could easily display the data in a SwiftUI list). The array is then sorted by descending date.
The update function stores the newAnchor that was received so that only changes are delivered in the future.
While testing I found that running the heart rate app on my watch, moving my test app into the background and then swapping back to it triggered the new heart rate data more quickly than just waiting for the new data to be delivered.
import Foundation
import HealthKit
struct HeartRateEntry: Hashable, Identifiable {
var heartRate: Double
var date: Date
var id = UUID()
}
class HeartHistoryModel: ObservableObject {
#Published var heartData: [HeartRateEntry] = []
var healthStore: HKHealthStore
var queryAnchor: HKQueryAnchor?
var query: HKAnchoredObjectQuery?
init() {
if HKHealthStore.isHealthDataAvailable() {
healthStore = HKHealthStore()
} else {
fatalError("Health data not available")
}
self.requestAuthorization { authorised in
if authorised {
self.setupQuery()
}
}
}
func requestAuthorization(completion: #escaping (Bool) -> Void){
let heartBeat = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
self.healthStore.requestAuthorization(toShare: [], read: [heartBeat]) { (success, error) in completion(success)
}
}
func setupQuery() {
guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
}
let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: .distantFuture, options: .strictEndDate)
self.query = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: queryAnchor, limit: HKObjectQueryNoLimit, resultsHandler: self.updateHandler)
self.query!.updateHandler = self.updateHandler
healthStore.execute(self.query!)
}
func updateHandler(query: HKAnchoredObjectQuery, newSamples: [HKSample]?, deleteSamples: [HKDeletedObject]?, newAnchor: HKQueryAnchor?, error: Error?) {
if let error = error {
print("Health query error \(error)")
} else {
let unit = HKUnit(from: "count/min")
if let newSamples = newSamples as? [HKQuantitySample], !newSamples.isEmpty {
print("Received \(newSamples.count) new samples")
DispatchQueue.main.async {
var currentData = self.heartData
currentData.append(contentsOf: newSamples.map { HeartRateEntry(heartRate: $0.quantity.doubleValue(for: unit), date: $0.startDate)
})
self.heartData = currentData.sorted(by: { $0.date > $1.date })
}
}
self.queryAnchor = newAnchor
}
}
}
I'm trying to build an iPhone app based on the user's live heart rate data.
I was able to get the latest heart rate from the Health App but once new information is recorded the app is not updated until relaunch.
Is there a way to get heart rate data in live? Maybe with HKObserverQuery?
here's my code so far: (Who manages to pull the last heartbeat)
import Foundation
import UIKit
import HealthKit
class HealthStore {
var healthStore: HKHealthStore?
init() {
if HKHealthStore.isHealthDataAvailable(){
healthStore = HKHealthStore()
}
}
func requestAuthorization(completion: #escaping (Bool) -> Void){
let heartBeat = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
guard let healthStore = self.healthStore else {return completion(false)}
healthStore.requestAuthorization(toShare: [], read: [heartBeat]) { (success, error) in completion(success)
}
}
func latestHarteRate(){
guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
}
let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictEndDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: [sortDescriptor]){(sample,result,error) in guard error == nil else{
return
}
let data = result![0] as! HKQuantitySample
let unit = HKUnit(from: "count/min")
let latestHr = data.quantity.doubleValue(for: unit)
print("Latest Hr\(latestHr) BPM")
healthStore?.execute(query)
}
You can use an HKAnchoredObjectQuery to create a query that returns an initial set of data and then updates to the data set.
Unfortunately, you can't provide a sort descriptor to the HKAnchoredObjectQuery, so you need to sort the data after you receive it if you don't want ascending order.
Here is a model object I created so that I could test in SwiftUI.
It creates an HKAnchoredQuery and sets an update handler function. The update handler converts the HealthKit results into my HeartRateEntry struct (This is so I could easily display the data in a SwiftUI list). The array is then sorted by descending date.
The update function stores the newAnchor that was received so that only changes are delivered in the future.
While testing I found that running the heart rate app on my watch, moving my test app into the background and then swapping back to it triggered the new heart rate data more quickly than just waiting for the new data to be delivered.
import Foundation
import HealthKit
struct HeartRateEntry: Hashable, Identifiable {
var heartRate: Double
var date: Date
var id = UUID()
}
class HeartHistoryModel: ObservableObject {
#Published var heartData: [HeartRateEntry] = []
var healthStore: HKHealthStore
var queryAnchor: HKQueryAnchor?
var query: HKAnchoredObjectQuery?
init() {
if HKHealthStore.isHealthDataAvailable() {
healthStore = HKHealthStore()
} else {
fatalError("Health data not available")
}
self.requestAuthorization { authorised in
if authorised {
self.setupQuery()
}
}
}
func requestAuthorization(completion: #escaping (Bool) -> Void){
let heartBeat = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
self.healthStore.requestAuthorization(toShare: [], read: [heartBeat]) { (success, error) in completion(success)
}
}
func setupQuery() {
guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
}
let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: .distantFuture, options: .strictEndDate)
self.query = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: queryAnchor, limit: HKObjectQueryNoLimit, resultsHandler: self.updateHandler)
self.query!.updateHandler = self.updateHandler
healthStore.execute(self.query!)
}
func updateHandler(query: HKAnchoredObjectQuery, newSamples: [HKSample]?, deleteSamples: [HKDeletedObject]?, newAnchor: HKQueryAnchor?, error: Error?) {
if let error = error {
print("Health query error \(error)")
} else {
let unit = HKUnit(from: "count/min")
if let newSamples = newSamples as? [HKQuantitySample], !newSamples.isEmpty {
print("Received \(newSamples.count) new samples")
DispatchQueue.main.async {
var currentData = self.heartData
currentData.append(contentsOf: newSamples.map { HeartRateEntry(heartRate: $0.quantity.doubleValue(for: unit), date: $0.startDate)
})
self.heartData = currentData.sorted(by: { $0.date > $1.date })
}
}
self.queryAnchor = newAnchor
}
}
}
I'm trying to get steps from half an hour ago and I'm using the method discussed here. The following is my code:
func getSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let calendar = Calendar.current
let halfHourAgoDate = calendar.date(byAdding: .minute, value: -30, to: now)
if let date = halfHourAgoDate {
let predicate = HKQuery.predicateForSamples(withStart: date, end: now, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { (_, result, error) in
var resultCount = 0.0
guard let result = result else {
completion(resultCount)
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: HKUnit.count())
return
}
DispatchQueue.main.async {
completion(resultCount)
}
}
healthStore.execute(query)
} else {
print("Error MainVC, date is being cast as nil")
}
}
When I actually try to get steps, this is my code:
var todaysSteps: Double = 0
getSteps(completion: { (resultCount) -> Void in
todaysSteps = resultCount
})
print(todaysSteps) // always comes out to 0.0
Yet whenever I run this code, the code returns 0.0. I've enabled HealthKit, and authorized it for steps, so I'm not sure what the reason is. Also note this is in my variable declarations:
let healthStore = HKHealthStore()
For anyone encountering the same issue, you need to do the code inside the completion handler, so:
var todaysSteps: Double = 0
getSteps(completion: { (resultCount) -> Void in
print(resultCount)
})
Hi i want to display on my app dayly step count from healthKit
This is my code:
imports
import UIKit
import HealthKit
Class instance
var healthStore = HKHealthStore()
viewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
if HKHealthStore.isHealthDataAvailable(){
let writeDataTypes = dataTypesToWrite()
let readDataTypes = dataTypesToWrite()
healthStore.requestAuthorization(toShare: writeDataTypes as? Set<HKSampleType>, read: readDataTypes as? Set<HKObjectType>, completion: { (success, error) in
if(!success){
print("error")
return
}
self.updateSteps()
})
}
}
Write:
func dataTypesToWrite() -> NSSet{
let stepsCount = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
let returnSet = NSSet(objects: stepsCount!)
return returnSet
}
Read:
func dataTypesToRead() -> NSSet{
let stepsCount = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
let returnSet = NSSet(objects: stepsCount!)
return returnSet
}
Now I want to create func updateSteps()
I have an answer to my question
func updateSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let startOfDay = Calendar.current.startOfDay(for: now)
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, end: now, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { (_, result, error) in
var resultCount = 0.0
guard let result = result else {
print("\(String(describing: error?.localizedDescription)) ")
completion(resultCount)
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: HKUnit.count())
}
DispatchQueue.main.async {
completion(resultCount)
}
}
healthStore.execute(query)
}