Search code examples
iosswiftdelegateswatchkithealthkit

How to set up an HKWorkoutSessionDelegate that is not an InterfaceController


Most of the HealthKit sample code uses an InterfaceController to serve as a HKWorkoutSessionDelegate however I'm trying to set up a HealthStoreManager class that will serve as a HKWorkoutSessionDelegate and take this business logic away from any of my individual InterfaceControllers responsibility, however the below code fails to start a workout, can anyone see my error?

 InterfaceController: {

         func segueToWorkoutInterfaceControllerWithContext() {

                // Create workout configuration
                let workoutConfiguration = HKWorkoutConfiguration()

                workoutConfiguration.activityType = .running
                workoutConfiguration.locationType = .outdoor  

                do {
                    let workoutSession = try HKWorkoutSession(configuration: workoutConfiguration)
                    let healthStoreManager = HealthStoreManager()
                    workoutSession.delegate = healthStoreManager
                    healthStoreManager.start(workoutSession)
                } catch {
                    fatalError(error.localizedDescription)
                }


                let contextDictionary = ["workoutConfiguration" : workoutConfiguration, "ActivityType": selectedActivityType] as [String : Any]

                // Pass configuration to next interface controller
                WKInterfaceController.reloadRootPageControllers(withNames: ["WorkoutControlsInterfaceController", "MainDisplayInterfaceController", "SpeedInterfaceController", "CaloriesAndDistanceInterfaceController"],
                                                                contexts: [contextDictionary],
                                                                orientation: .horizontal,
                                                                pageIndex: 1)
            }

        }
    }


class HealthStoreManager: NSObject, CLLocationManagerDelegate, HKWorkoutSessionDelegate {
 private let healthStore = HKHealthStore()

// MARK: - HKWorkoutSessionDelegate

    func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
        print("workout session did fail with error: \(error)")
    }

    func workoutSession(_ workoutSession: HKWorkoutSession,
                        didChangeTo toState: HKWorkoutSessionState,
                        from fromState: HKWorkoutSessionState,
                        date: Date) {
        DispatchQueue.main.async {
            self.handleWorkoutSessionState(didChangeTo: toState, from: fromState)
        }
    }

    func workoutSession(_ workoutSession: HKWorkoutSession, didGenerate event: HKWorkoutEvent) {
        DispatchQueue.main.async {
            //self.healthStoreManager.workoutEvents.append(event)
        }
    }

    private func handleWorkoutSessionState(didChangeTo toState: HKWorkoutSessionState,
                                           from fromState: HKWorkoutSessionState) {
        switch (fromState, toState) {
        case (.notStarted, .running):

            print("workout started")

        case (_, .ended):


        default:
            break
        }


    }


}

Solution

  • I found my error, I needed to pass the healthStoreManager instance into the context in order to avoid this class from being deallocated:

      let contextDictionary = ["workoutConfiguration" : workoutConfiguration, "ActivityType": selectedActivityType, "healthStoreManager" : healthStoreManager] as [String : Any]