The fatal error is (or was) caused by this line:
let wSM = WorkoutSessionManager()
That line creates a new instance of WorkoutSessionManager and calls init()
on it.
Swift provides a default initializer called init()
for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. But WorkoutSessionManager does not provide default values for the healthStore
and workoutSession
properties (and those properties are not optionals), and it provides its own initializer named init(context:)
, so it has no default initializer.
You need to either create your instance of WorkoutSessionManager using the designated initializer init(context:)
(passing an appropriate instance of WorkoutSessionContext) or provide a default initializer for WorkoutSessionManager named init()
.
The precise manner in which you should do the former depends on the implementation of the rest of your app and the presentation of your DashboardController. I assume you are trying to recreate the "Fit" app shown in WWDC 2015 Session 203.
In that demonstration, the initial controller is an instance of ActivityInterfaceController, and that controller is responsible for presenting the next interface (via segues created in the storyboard). You can see the following code in the ActivityInterfaceController class:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
let activityType: HKWorkoutActivityType
switch segueIdentifier {
case "Running":
activityType = .Running
case "Walking":
activityType = .Walking
case "Cycling":
activityType = .Cycling
default:
activityType = .Other
}
return WorkoutSessionContext(healthStore: self.healthStore, activityType: activityType)
}
The function above creates and returns a new instance of WorkoutSessionContext using an instance of HKHealthStore held by the initial controller. The context returned by that function is passed to the destination interface controller for the relevant segue through awakeWithContext
.
For transitions in code, you can pass a context instance using equivalent functions such as pushControllerWithName(context:)
which also lead to awakeWithContext
.
If your initial controller is similar to the above, you can access the passed context in awakeWithContext
in your DashboardController class and use it to configure a new instance of WorkoutSessionManager:
class DashboardController: WKInterfaceController
{
// ...
var wSM: WorkoutSessionManager?
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if context is WorkoutSessionContext {
wSM = WorkoutSessionManager(context: context as! WorkoutSessionContext)
}
addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}
// ...
}
Creating an instance of WorkoutSessionManager in that way avoids calling the (non-existent) init()
initializer and permits reuse of the HKHealthStore instance. Whether that approach is open to you depends on the rest of your code and the way you are presenting your DashboardController.
Note that you should avoid creating multiple instances of WorkoutSessionManager. Use a singleton to provide a single instance of WorkoutSessionManager that is shared across your extension.