The Mushometer Watch App

2015/6/9

Mushometer 3.4 includes an Apple Watch App and Glance. Now that the updated app has passed review I wanted to write a bit about my approach to communicating between the iPhone App and the Apple Watch.

Yes, there are much better solutions to this problem like MMWormhole, but I wanted to make my own run at something simple and effective. If you are reading this after the release of iOS 9 and watchOS 2, be advised that his approach will not work without modification.

Mushometer already had a today widget extension that displays your current season total distance and could be used to launch the app. To accomplish that I am using app groups and writing to a shared defaults that is monitored by the today widget. This works pretty well for a simple data update. While I was thinking about the Mushometer watch app I decided I would try to use the same approach since watchkit apps (before watchOS 2) are very similar to a today widget extension. Also this gave me the added benefit of showing the same speed, distance and elapsed time information when the app was Active on the iPhone.

This was pretty straight forward to accomplish. To update the elapsed time or when a new location comes in from GPS, I update the data in the shared defaults. When the today widget is visible to the user I periodically check the shared defaults for updated data and update the UI of the widget. Dead simple.

The Apple Watch Glance and App are only slightly more complicated but the approach is the same. Using performSelector:withObject:afterDelay: I check the shared defaults for new data. Since we only do these checks when the App or Glance are visible, and we know that the update frequency doesn't have to me any more frequent than once each second, this approach works pretty well, though I wouldn't call it elegant.

To communicate back to the containing iPhone App when the user presses the pause button I use [WKInterfaceController openParentApplication:reply:^]. However, this is deprecated in watchOS 2 though so I'll have to change this if I want to update.

On the iPhone App side of things I created a helper class that contains all the code necessary to update the data in the shared defaults group used by the Today Widget and the Watch App. Here's a example for updating the speed and distance information:

+(void)setGPSSpeed:(NSString*)s andDistance:(NSString*)d
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] 
            initWithSuiteName:@"group.MushometerWidget"];
        [sharedDefaults setBool:YES forKey:@"isRecording"];
        [sharedDefaults setObject:s forKey:@"speed"];
        [sharedDefaults setObject:d forKey:@"distance"];
        [sharedDefaults synchronize];
    }
}

When adding a Watchkit app target to your existing project, Xcode will create all the necessary controllers for your App and Glance with the associated Storyboards. Since the Mushometer Glance and App display all the same information, I created a subclass of WKInterfaceController with all the common functionality. This made keeping the functionality of both views in sync very simple. From there I use this Class as the base for the InterfaceController and GlanceController.

When it comes to the UI, there are three modes in the Mushometer Watch App interface; Season Distance, Recording and Saving. Here's the UI with everything visible.

Storyboard Scene

To change the display I simply hide or show the appropriate groups. This works for the simple interface of the Mushometer Watch App. If your interface includes hirericial navigation or groups with many images or elements you should probably use a more complex storyboard flow rather than trying to load everything on launch. Speed is the key.

There were 12 sessions at WWDC that touched on watchOS and designing for the Apple Watch. I'd recommend watching all of them.

I still haven't figured out how people will use the Watch App and hold onto a dogsled at the same time but that is why I've kept things simple for now.




Visit the Archive for more posts.