top of page

Speed-Pace Converter for iOS and watchOS

Problem(s)

I run.

I can't divide by 60 in my head very well.

In other words, when I'm in my run, or more likely -- planning my run, I want to think in both speed (miles-per-hour) as well as pace (minutes & seconds per mile). The math is fairly easy -- essentially divide by 60, but that is not an easy thing to do in my head, so I would like an app to do that for me.

I am also looking for an opportunity to get back to iOS/watchOS development, particular Swift/SwiftUI.

Requirements

I want this on my Apple Watch so that it is with me all the time when I'm running, as I do not run with my phone.

The calculator app on the watch is not good enough, because when I get the answer "9.2 minutes per mile" on the calculator, I don't want to do the additional step of converting .2 minutes to 12 seconds.

I want to use and support the latest Apple devices and development platform: Xcode 12, Watch Series 6, Swift/SwiftUI, etc.

Current State

I built this app a while ago (mid-2018), but I haven't touched it in a while. It was built with the storyboard approach and touch-force popup menus with swift code behind. It wasn't a great interface, and opening it with the latest Xcode yielded a lot of errors. Because of the outrageous age (3 years!) of this code, and the fact that the logic is fairly simple ("divide by 60 and unit conversion") I want to throw out my original prototype app and start over in order to learn new tooling (SwiftUI) as well as lessons-learned along the way. I also want to build a standalone watch app (something not possible in 2018).


Old UI

Old UI - iOS Storyboard / Layout

Old UI - watchOS Storyboard / Layout

Plan

Training


I went through Apple's SwiftUI Tutorial. It was a pretty good tutorial. I was super-impressed with how little code it takes to create something that looks and works great. That being said, knowing what limited code to write will be the challenge for me. I'm sure I will fumble through a lot of different layout options and such until I hit upon just the right thing -- but I'm dedicated to finding the right and concise code to write and gaining understanding rather than settling for something that sorta kinda works most of the time without knowing why.

New UI

...and that's where I'm at today. I will start creating the Xcode project shortly.


Development

Update Jul 09 2021

I've been working on this project off-and-on for several weeks now. Here are some random thoughts about how it's going so far.

  • Interface builder is better than it used to be. It still has lots of quirks, but I'm pleasantly surprised by how much of the interface I can develop without actually running the simulator.

  • I'm getting better with Swift. The syntax is becoming more familiar and natural-feeling.

  • I have extended my iOS/watchOS development training through Andrew Bancroft's iOS 14 Getting Started course at Pluralsight

  • I'm actually fairly happy with the watch app (for my own needs). I can see how others might feel lost in it, so I need input from other people on whether (how) to fix-up the UI to make it easier to use.

  • I slapped an iOS app together too -- still early stages of development here. It actually seems easier to get input from the user via the watch's digital crown rather than through text input/keyboard/etc on the iOS device. I am currently using a mix of sliders and pickers for input instead of text/keyboard input because all of the solutions I have found so far seem pretty hacky. That really surprised me. In a similar vein it is weird to see some of the limitations that SwiftUI doesn't provide for common scenarios yet that must be handled via UIKit.

  • As always, the App's icon is horrible -- again, I'm not a designer. It's also still painful to deal with all the different icon sizes Apple wants.

  • The SwiftUI Views/Navigation setup makes a lot more sense to me than storyboards. I never liked the "outlets" that were created by click-drag-n-drop. I like to see all of my code in text (diffs, reading, debugging, choice of tools, etc), and that drag-and-drop mapping always felt weird to me.

  • In order to actually deploy to my iOS 14.6 device I had to update Xcode, which also meant upgrading macOS from Catalina to Big Sur. All of that updating took a few days... lesson learned is do NOT use the App Store for updating Xcode -- delete it, download the .xip from Apple's developer website and install it manually.

Well, that's a brain dump so far. I'm sure I'm forgetting some good points since I haven't updated this page in several weeks and had holidays and vacations over that period too.

Current Status



Main Screen

Main ScreenThis is the main watch screen. The user can tap on any value (speed, pace, distance or elapsed time). When they do the value changes to the accent color (orange). The digital crown can be used to adjust the value.

  • When the user changes speed, pace and elapsed time are updated.

  • When the user changes pace, speed and elapsed time are updated.

  • When the user changes distance, elapsed time is updated.

  • When the user changes elapsed time, distance is updated.



ListView

The List screen shows some "standard" or predefined values based on activity type. For example, selecting Hike shows 1.0 mph, 2.0 mph, etc. up to 6.0 mph, whereas selecting Bike shows 3.0mph to 20.0 mph. I need to decide what values make sense for each activity to make this more useful.


Settings

The settings screen is mostly aspirational at this point. Only imperial units are implemented, and the values selected in this screen don't apply to the rest of the app (yet) or get saved for the user.


iOS Screen

The iOS screen doesn't have much planning behind it at this point. It is certainly not intuitive -- it has a bunch of junk on it. Usability is bad too -- for example, if I want to change the speed value and glance at the pace to see how it is changing, well, it's awkward because my finger is on the slider meaning my hand is covering the pace value. I need to find alternative layouts or controls.


It also LOOKS more complicated than the watch screen because of the variety and complexity of user-input controls being used.


One good thing... I was able to throw the List view at the bottom of the iOS screen with zero changes to the list view that I built for the watch app -- that felt good!













Update Jul 21 2021


I have made a lot of progress on the watch app:

  • Settings screen works, and persists values

  • The other screens respond to the changes, namely...

  • Support for metric units (kilometers)

I dare say the watch app is "feature complete". The only thing really remaining is to make the predefined list of values in the List View more meaningful (especially the metric values). I'm sure I will look back at this claim of "feature complete" later and do a facepalm (I'm already thinking about how to sync settings between iOS and watchOS apps, adding color preferences, more code cleanup, adding tests ...)


Things I Learned and Approaches Taken

  • Refactored and renamed a couple of classes (and converted to structs), organized "constants"

  • Made the data behind the "dynamic" screen an environment object in order for changes to settings to apply to that data/screen.

  • Learned a lot about and used UserDefaults and @AppStorage, and protocol RawRepresentable to get settings values like "distance" (a Measurement struct object) into UserDefaults

  • Learned more about MeasurementFormatter in order to get the displayed values to show correctly. MeasurementFormatter is kinda cool in that if the value is 5.0km it will tell me (in locale en_US) user "3.1 miles", but in this case I want to ignore locale and display in the units I want, so setting formatter.unitOptions = .providedUnit is the way to go. I initially started down the "change the locale" route but that felt really wrong and looked for a better option, and I think unitOptions is that better option in this case.

  • Enhanced the underlying data structure by converting the "distance" as a double-assuming-miles-are-the-units to a Measurement<UnitLength> value.

Update Jul 22 2021


I updated the values for the Predefined values in the list view, particularly the metric values.

I also focused a bit on the iOS App:

  • Toyed with various input controls for speed and distance (original slider, attempted a custom vertical slider control, picker with discrete values, text input, etc). None of them felt as good as the slider, so sticking with it.

  • Removed direct input for pace (they layout was weird... tiny "speed" portion and huge "pace" portion). I'm thinking through ways for the user to directly input pace.

  • Converted the app to a tabbed navigation setup. Moved the List view to the second tab.

  • Added icons for the Hike/Run/Bike options (also impacts watchOS app).

  • In general I think this is more usable and "feels" better than the first iteration that was really cluttered and confusing. Although I still fully admit I'm not a UX designer, so even this iteration can use some work.




Update Jul 23 2021


I added the settings page to the iOS app. This took very little time (less than an hour?) -- I feel like (knock-on-wood) development is speeding up for me. I'm at the stage where I'm no longer asking "how do I do this..." but instead saying "I know how to do this... what's the exact syntax again?"



Update Jul 26 2021


Updated the app icon. Found a new icon, colored it, created the icon set. Also added "acknowledgements" to settings to give the icon author credit as requested.


Naming things well is very important. I changed the name from "calculator" to "converter". Even though it is a major pain with no/very little end-user impact, I went ahead and changed the name of everything. The time to change the name is before you publish/release something -- otherwise it is much more difficult to do.

I tightened up the watchOS dynamic entry view. There were excessive and unnecessary containers (ScrollViews, Groups, etc.) that I could remove fairly easily. This has the added benefit of when the dynamic entry screen is activated, the digital crown acts on the speed value, and does not scroll the view by default. Also fixed a bit of spacing.

More project organization. Moving files to where they belong, renaming things, etc. General cleanup.


Modularized the iOS Dynamic view, and created different layouts for portrait vs landscape (ahem, I mean regular vs compact size classes).


Next Steps

  • Input from other users

  • Add in-app purchasing (for dynamic calculations screen only?)

    • Must join Apple Developer Program to do this.

  • Publish on App Store!


Comentarios


bottom of page