I’ve been working with WatchKit lately and was rewriting some old code for better navigation handling.
When creating a watchOS app using WatchKit, we use Interface Builder for creating
For identifying that particular screen while handling the navigation programmatically, we provide the custom class for it and the identifier associated with that screen.
Here, we use hard-coded strings for the identifier, and I sometimes mistype them in code while presenting/pushing the controller or reloading them. So I used a more type-safe approach of using enums and an extension on
WKInterfaceController that I want to share.
I’m creating a clone of the workout app on the Apple watch. I start with the first screen, which lists the type of workouts in a carousel style.
Let’s name this controller as
When clicking on any of the activities, it opens a timer screen which opens three paged screens. Those screens contain the details of the workout, controls for it, and the now playing screen.
I name the timer controller as
The page style screens can be named as
These will be the identifiers for defining the
WKInterfaceController in Interface Builder and identifying them while writing code.
When you click on a particular workout, it reloads the screen with a single screen controller for showing the timer.
The timer screen after the countdown reloads the screen with page-based controllers.
For that, we use the method—
The standard approach would be calling this function as -
WKInterfaceController.reloadRootPageControllers(withNames: ["WorkoutControlsInterfaceController", "WorkoutStatisticInterfaceController", "NowPlayingInterfaceController"], contexts: [context], orientation: .horizontal, pageIndex: 1)
And oops. This gives an error-
Error — interface does not define view controller class WorkoutStatisticInterfaceController
The error happened because I mistyped the name of the identifier. And the probability of such mistakes happening increases when you have 10–20 of such controllers.
So, let’s try a more type-safe way using enum!
I start with an enum
InterfaceIdentifier and add identifiers as cases.
If I have to be honest, the primary reason for working on making navigation easier was to get the dot notation.
I wrote a method to reload one single root screen and page-paged screens that take
InterfaceControllerName as a parameter.
I used three examples in my sample project:
- For going from the workout list screen to the timer screen, I use —
- For proceeding from the timer screen to the main workout screens, I use —
reload(withNames: [.workoutControls, .workoutStatistics, .nowPlaying], context: context, pageIndex: 1)
- For ending the workout and going back to the home screen, I use —
reload(withName: .workoutList, context: context)
I feel this eliminated all the bugs related to mistyping identifiers, and I also love dot notation for getting the list of all available controllers.
I’m still a beginner in this massive Apple platforms development world, so any feedback or a better solution is much appreciated in the comment section!