Im trying to make a workout app where the user's heart rate is being displayed on the Apple Watch. I've been following Apple's WWDC "New Ways to Work with workouts" vide. Here's the link https://developer.apple.com/videos/play/wwdc2018/707/?time=615
Anyway, every time I try to run the app I keep getting the error "Thread 1: Fatal Error unexpectedly found nil while unwrapping an optional value "
session = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
I've tried adding a a question mark(?) after the "try" but all that does is prevent the app from crashing and doesn't start the workout. Here's the full code. P.S. I am fairly new to Swift and I'm finding it incredibly frustrating that there isnt much example code for the new HealthKit yet. (I know its fairly new, but still frustrating :D). Thanks for the help
class InterfaceController: WKInterfaceController, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate {
let healthStore = HKHealthStore()
var configuration: HKWorkoutConfiguration!
var session: HKWorkoutSession!
var builder: HKLiveWorkoutBuilder!
func startWorkoutWithHealthStore(){
// configuration.activityType = .crossTraining
// configuration.locationType = .indoor
do {
session = try? HKWorkoutSession(healthStore: healthStore, configuration: configuration)
} catch {
// let the user know about the error
return
}
builder = session.associatedWorkoutBuilder()
//Setup session and builder
session.delegate = self
builder.delegate = self
builder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)
//Start Session & Builder
session.startActivity(with: Date())
builder.beginCollection(withStart: Date()) { (success, error) in
self.setDurationTimerDate() //Start the elapsed time timer
}
}
@IBAction func startButtonClicked() {
print("Start BTN clicked")
startWorkoutWithHealthStore()
}
//Track Elapsed Time
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder){
print("Collection Started")
setDurationTimerDate()
}
func setDurationTimerDate(){
print(", duration timer started"
)
//Create WKInterfaceTimer Date
let timerDate = Date(timeInterval: -self.builder.elapsedTime, since: Date())
DispatchQueue.main.async {
self.timer.setDate(timerDate)
}
//Start or stop timer
let sessionState = self.session.state
DispatchQueue.main.async {
sessionState == .running ? self.timer.start() : self.timer.stop()
}
}
// MARK: HKLiveWorkoutBuilderDelegate
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>){
for type in collectedTypes{
guard let quantityType = type as? HKQuantityType else {
return // Do nothing
}
let statistics = workoutBuilder.statistics(for: quantityType)
//let label = labelForQuantityType(quantityType)
// updateLabel(wkLabel, withStatistics: statistics)
print(statistics as Any)
}
}
// MARK: State Control
func stopWorkout(){
session.end()
builder.endCollection(withEnd: Date()) { (success, error) in
self.builder.finishWorkout(completion: { (workout, error) in
self.dismiss()
})
}
}
}
You shouldn't mask the error into an
Optional
by usingtry?
thrown byHKWorkoutSession(healthStore: healthStore, configuration: configuration)
, especially if you already put the statement in ado-catch
block. You get a crash, becausesession
is defined as an implicitly unwrapped optional (!
mark after the type), which it shouldn't be.You should define
session
as a normal optional if it might have anil
value and safely unwrap/optional chain it every time you access it.