I can't get any calories/activeEnergyBurned to show up in my app, and don't know why?
WorkoutInterfaceController
:
private func totalCalories() -> Double {
return totalEnergyBurned.doubleValue(for: HKUnit.kilocalorie())
}
private func setTotalCalories(calories: Double) {
totalEnergyBurned = HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories)
}
func startQuery(quantityTypeIdentifier: HKQuantityTypeIdentifier) {
let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictStartDate)
let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
let queryPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate, devicePredicate])
let updateHandler: ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void) = { query, samples, deletedObjects, queryAnchor, error in
self.process(samples: samples, quantityTypeIdentifier: quantityTypeIdentifier)
}
let query = HKAnchoredObjectQuery(type: HKObjectType.quantityType(forIdentifier: quantityTypeIdentifier)!,
predicate: queryPredicate,
anchor: nil,
limit: HKObjectQueryNoLimit,
resultsHandler: updateHandler)
query.updateHandler = updateHandler
healthStore.execute(query)
activeDataQueries.append(query)
}
func process(samples: [HKSample]?, quantityTypeIdentifier: HKQuantityTypeIdentifier) {
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self, !strongSelf.isPaused else { return }
if let quantitySamples = samples as? [HKQuantitySample] {
for sample in quantitySamples {
if quantityTypeIdentifier == HKQuantityTypeIdentifier.activeEnergyBurned {
let newKCal = sample.quantity.doubleValue(for: HKUnit.kilocalorie())
strongSelf.setTotalCalories(calories: strongSelf.totalCalories() + newKCal)
print("NewKCal: \(newKCal)")
print("TotalCalories: \(strongSelf.totalCalories())")
}
}
strongSelf.updateLabels()
}
}
}
The log prints out '0' no matter how long I run the app for.
I've tested on the Simulator and on Device.
Per a question, here is the code for saving workout data:
private func saveWorkout() {
// Create and save a workout sample
let configuration = workoutSession!.workoutConfiguration
let isIndoor = (configuration.locationType == .indoor) as NSNumber
print("locationType: \(configuration)")
let workout = HKWorkout(activityType: configuration.activityType,
start: workoutStartDate!,
end: workoutEndDate!,
workoutEvents: workoutEvents,
totalEnergyBurned: totalEnergyBurned,
totalDistance: nil,
metadata: [HKMetadataKeyIndoorWorkout:isIndoor]);
healthStore.save(workout) { success, _ in
if success {
self.addSamples(toWorkout: workout)
}
}
// Pass the workout to Summary Interface Controller
WKInterfaceController.reloadRootControllers(withNames: ["SummaryInterfaceController"], contexts: [workout])
}
private func addSamples(toWorkout workout: HKWorkout) {
// Create energy and distance samples
let totalEnergyBurnedSample = HKQuantitySample(type: HKQuantityType.activeEnergyBurned(),
quantity: totalEnergyBurned,
start: workoutStartDate!,
end: workoutEndDate!)
// Add samples to workout
healthStore.add([totalEnergyBurnedSample], to: workout) { (success: Bool, error: Error?) in
if success {
// Samples have been added
print("Samples have been added")
}
}
}
Another source of calorie confusion is that the Active Calories total in the Health app is not the same as the Move ring calories total in the Apple Watch's Activity app. This is because, unlike the Health app, the Activity app does not include data from multiple sources — it only uses data from your Apple Watch.
Check your fitness tracking settings: On your iPhone, in the Watch app, go to: My Watch (tab) > Privacy - turn on Heart Rate and Fitness Tracking. On your iPhone, go to: Settings > Privacy > Motion & Fitness - check that both Fitness Tracking and Health are enabled.
It includes calories consumed while you were walking or exercising as well as calories consumed when you were in the rest state. The Move calories on the other hand, are only calories that are associated with activity. These are energy calories that were burned when you were walking around or 'moving' around.
You can do it another way without using predicate.
weak var delegate: WorkoutSessionManagerDelegate?
let healthStore: HKHealthStore
var workoutSession: HKWorkoutSession
var workoutStartDate: NSDate?
var workoutEndDate: NSDate?
var queries: [HKQuery] = []
var activeEnergySamples: [HKQuantitySample] = []
var distanceSamples: [HKQuantitySample] = []
var heartRateSamples: [HKQuantitySample] = []
let energyUnit = HKUnit.calorieUnit()
let distanceUnit = HKUnit.meterUnit()
let countPerMinuteUnit = HKUnit(fromString: "count/min")
var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
let activeEnergyType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!
let heartRateType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)! // 1/3
var distanceType: HKQuantityType {
if self.workoutSession.activityType == .Cycling {
return HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceCycling)!
} else {
return HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!
}
}
var currentActiveEnergyQuantity: HKQuantity
var currentDistanceQuantity: HKQuantity
var currentHeartRateSample: HKQuantitySample?
init(context: WorkoutSessionContext) {
self.healthStore = context.healthStore
self.workoutSession = HKWorkoutSession(activityType: context.activityType, locationType: context.locationType)
self.currentActiveEnergyQuantity = HKQuantity(unit: self.energyUnit, doubleValue: 0.0)
self.currentDistanceQuantity = HKQuantity(unit: self.distanceUnit, doubleValue: 0.0)
super.init()
self.workoutSession.delegate = self
}
// MARK: Active Energy Burned Streaming
func createActiveEnergyStreamingQuery(workoutStartDate: NSDate) -> HKQuery? {
print("Active energy query started")
// ** Creating a match samples predicate to sum the data is no longer the convention **
// Sum the new quantities with the current active energy quantity.
guard let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned) else {return nil}
// Instantiate a HKAnchoredObjectQuery object with a results handler that calls our sumEnergyBurnedSamples function
let activeEnergyQuery = HKAnchoredObjectQuery(type: quantityType, predicate: nil, anchor: anchor, limit: Int(HKObjectQueryNoLimit)) { (query, samples, deletedObjects, newAnchor, error) -> Void in
guard let newAnchor = newAnchor else {return}
self.anchor = newAnchor
self.addActiveEnergySamples(samples)
}
// Results handler that calls our addActiveEnergySamples function
activeEnergyQuery.updateHandler = {(query, samples, deletedObjects, newAnchor, error) -> Void in
self.anchor = newAnchor!
self.addActiveEnergySamples(samples)
}
return activeEnergyQuery
}
func addActiveEnergySamples(samples: [HKSample]?) {
print("Updating calorie samples")
guard let activeEnergyBurnedSamples = samples as? [HKQuantitySample] else { return }
// addActiveEnergySamples method dispatches back to the main queue
dispatch_async(dispatch_get_main_queue()) {
// Adds the new active energy sample to the running total
self.currentActiveEnergyQuantity = self.currentActiveEnergyQuantity.addQuantitiesFromSamples(activeEnergyBurnedSamples, unit: self.energyUnit)
// Adds that sample to an array of samples accumulated over the workout
self.activeEnergySamples += activeEnergyBurnedSamples
// Whenever new samples become available, call the corresponding delegate method. This updates the UI with new samples.
self.delegate?.workoutSessionManager(self, didUpdateActiveEnergyQuantity: self.currentActiveEnergyQuantity)
// Print checks
guard let sample = activeEnergyBurnedSamples.first else{return}
let value = sample.quantity.doubleValueForUnit(self.energyUnit)
print(value)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With