getting HeartbeatSeriesSamples in iOS swift HealthKit ... HKAnchoredObjectQuery fails but HKSampleQuery works

56 Views Asked by At

Developing a mirrored watch/iPhone app that needs to see the beat to beat timing. This video from apple claims to show how to do this: https://developer.apple.com/videos/play/wwdc2019/218/?time=1513 at 28.26.

the answer provided here Apple Health initHeartBeatSeries , how to get HKHeartbeatSeriesSample? using HKSampleQuery works but gives a one shot answer. I want a continuous update whilst the app is running.

When I try a HKAnchoredObjectQuery it fails with: "*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid data types 'HKDataTypeIdentifierHeartbeatSeries' for _HKWorkoutComparisonFilter.workoutType: Error Domain=com.apple.healthkit Code=3 "'HKDataTypeIdentifierHeartbeatSeries' not found in allowed data types classes (HKWorkoutType)" UserInfo={NSLocalizedDescription='HKDataTypeIdentifierHeartbeatSeries' not found in allowed data types classes (HKWorkoutType)}'"

My code based somewhat on apples documentation is:

// this should get initial batch of samples and then have update called as they come in
    func getBeatSamples( startDate: Date ) -> Void {
        
        let heartbeatSeriesSampleType = HKSeriesType.heartbeat()
        
        var myAnchor : HKQueryAnchor? = HKQueryAnchor(fromValue: 0)
        
        let todayPredicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: [])
        let cyclingPredicate = HKQuery.predicateForWorkouts(with: .cycling)
        let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [todayPredicate, cyclingPredicate])
        
 
        let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> () = { (query, samplesOrNil, deletedObjectsOrNil, newAnchor, errorOrNil) in
            
            guard errorOrNil == nil else {
                // error
                return
            }
            
            guard let samples = samplesOrNil, let deletedObjects = deletedObjectsOrNil else {
                // Properly handle the error.
                return
            }
            
            myAnchor = newAnchor
            
            //for beatSample in samples {
                // Process the new heart rate samples.
                if let heartbeatSeriesSample = samples.first as? HKHeartbeatSeriesSample {
                    let queryBeats = HKHeartbeatSeriesQuery(heartbeatSeries: heartbeatSeriesSample) { (query, timeSinceSeriesStart, precededByGap, done, error) in
                        guard error == nil else {
                            // error
                            return
                        }
                        print(timeSinceSeriesStart)
                    }
                    self.healthStore.execute(queryBeats)
                }
            //}
            
            // Copilot version
            if let coSamples = samplesOrNil as? [HKHeartbeatSeriesSample]
            {
                for sample in coSamples {
                    print("coSample: \(sample)")
                }
            } else {
                // handle error
            }
                            
            for deletedStepCountSamples in deletedObjects {
                // Process the deleted step count samples.
            }
            
            // The results come back on an anonymous background queue.
            // Dispatch to the main queue before modifying the UI.
            
            DispatchQueue.main.async {
                // Update the UI here.
            }
        }
        
//***** fails on this line
        let query = HKAnchoredObjectQuery(type: HKSeriesType.heartbeat(), 
                                          predicate: compoundPredicate,
                                          anchor: myAnchor,
                                          limit: HKObjectQueryNoLimit,
                                          resultsHandler: updateHandler)
        
        query.updateHandler = updateHandler
        healthStore.execute(query)

    }

The readData setup for the workout is:

let typesToRead: Set<HKObjectType> = [
        HKQuantityType(.heartRate),
        
        // Beat addition
        HKSeriesType.heartbeat(),
        HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!,       
        // end beat addition
        
        HKQuantityType(.activeEnergyBurned),
        HKQuantityType(.distanceWalkingRunning),
        HKQuantityType(.cyclingSpeed),
        HKQuantityType(.cyclingPower),
        HKQuantityType(.cyclingCadence),
        HKQuantityType(.distanceCycling),
        HKQuantityType(.dietaryWater),
        HKQuantityType.workoutType(),
        HKObjectType.activitySummaryType()
    ]
0

There are 0 best solutions below