Search code examples
swiftwatchkit

Is it possible to query live HKUnit data directly from another class without passing data?


I have a custom WorkoutSessionManager.swift that does not subclass WKInterfaceController. It has its own context and protocol. It contains the query and sample updates. All the heart rate, distance and energy data (HKUnits) are printing to the console. This simple block outputs the data to the console.

guard let sample = activeEnergyBurnedSamples.first else{return}
        let value = sample.quantity.doubleValueForUnit(self.energyUnit)
        print(value)

I have a separate Dashboard.swift with my mi, Cal, bpm labels.

Since it is live data is it possible to query the HK data directly without passing this value property?

If this isn't possible how should I accomplish sending the value to my labels in the external class?

enter image description here enter image description here

WorkoutSessionManager.swift

func createActiveEnergyStreamingQuery(workoutStartDate: NSDate) -> HKQuery? {

    guard let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned) else {return nil}
    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)
    }
    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 }

    dispatch_async(dispatch_get_main_queue()) { 

        self.currentActiveEnergyQuantity = self.currentActiveEnergyQuantity.addQuantitiesFromSamples(activeEnergyBurnedSamples, unit: self.energyUnit)
        self.activeEnergySamples += activeEnergyBurnedSamples
        self.delegate?.workoutSessionManager(self, didUpdateActiveEnergyQuantity: self.currentActiveEnergyQuantity)

        // Checks
        guard let sample = activeEnergyBurnedSamples.first else{return}
        let value = sample.quantity.doubleValueForUnit(self.energyUnit)
        print(value)
    }
}

DashboardController.swift

@IBOutlet weak var milesLabel: WKInterfaceLabel!
@IBOutlet weak var caloriesLabel: WKInterfaceLabel!
@IBOutlet weak var bmpLabel: WKInterfaceLabel!

Solution

  • If I understand correctly, you want to query the HealthKit store for certain kinds of samples, and update your UI every time new samples are saved to the store.

    You can do this in several ways, including by using delegation, closures, or notifications. See below for sample code using delegation.

    You've already defined a WorkoutSessionManagerDelegate protocol in your WorkoutSessionManager class (shown here). If that protocol is the same as that used in WWDC 2015 Session 203, it provides methods such as didUpdateActiveEnergyQuantity, didUpdateDistanceQuantity, and didUpdateHeartRateSample. If you give WorkoutSessionManager an object that acts as a delegate, the manager can delegate UI management to that object using the methods provided by the delegation protocol.

    In the principal class, WorkoutSessionManager, define a property to hold a weak reference to the delegate: weak var delegate: WorkoutSessionManagerDelegate?

    Then, whenever new samples become available, call the corresponding delegate method. So, for example, in the addActiveEnergySamples function, you already have the following line: self.delegate?.workoutSessionManager(self, didUpdateActiveEnergyQuantity: self.currentActiveEnergyQuantity)

    In the delegate class, DashboardController, adopt the WorkoutSessionManagerDelegate protocol: class DashboardController: WKInterfaceController, WorkoutSessionManagerDelegate

    And in awakeWithContext, assign yourself as the manager's delegate: wSM?.delegate = self

    Finally, in the delegate class, implement the methods provided by the delegation protocol, and make such changes to your UI as necessary based on the data passed through those methods.