The latest version can be downloaded on the App Store
In summer of 2017, I dropped 45 lbs while at a 6-week math program.
I achieved this by going to the gym 3 hours a day and eating nothing but salad and chicken breast. Aside from lifting weights and playing basketball, I ran gruelingly on treadmills at the gym, usually for an hour a day, on 12 x five minute HIIT intervals with each being a 1 min x 11 miles / hour sprint + 4 mins x 7 miles /hour jog.
As I recount, the journey could’ve been more efficient and less mentally-traumatizing had my routine been switched out every now and then. I tried running mornings around OSU campus for the first week, but two dealbreakers caused me to stop:
Every time I saw the fork in front of the science building, I knew I was about halfway through, but rather than rejoice, I would sulk from forecasting what’s about to come next. For those who think uncertainty is always worse than certainty, try to be a long distance runner.
I thought, if only there was an easy way for me to be given new routes of the same difficulty so I could explore the campus and never run the same one twice. I realized some fellow morning runners would mentally chart out a route (by remembering key turns), but I didn’t have much of a memory ability, especially during aerobic levels of exercise.
With no software experience, I put this on hold until last summer’s Make School Summer Academy, when I did it for a weeklong hackathon.
- The route became too mundane the marginal benefit dwindled until the marginal cost (being scolded by an RA, getting hit by a car) and opportunity cost (calculating a new route that’s runnable and of the same distance and memorizing it by foot) became too high. I ended up just running at the gym next to the dorm.
- The mental expenditure per route increased over time. The initial excitement for running the path less taken over time was replaced by .
I imagined an ideal service that:
- Constantly generate new routes that loop back to the same starting point
- Be customizable and accurate to input distance
- Easily provide turn-by-turn navigation for ease of use
As I learn about the API ecosystem, I researched a number of APIs:
Core Location – iOS Location Manager for live location in Swift
MapBox Directions and Navigation SDK – Navigation API that displays a route from one street intersection to the other, used in my REST API
GeoNames – API that binds any (lat, long) coordinate to the nearest street intersection
Heroku – Deployment of my REST API, which generates routes from distance, longitude and latitude input
You may question why I needed such a backend rather than just generate the routes in Swift. Firstly, I wanted to deploy a REST API for the first time. Secondly, at that time, I thought the app could go cross-platform, and rather than worry about persistence using CoreData or ORM tools for Android, I could do it simply with a Flask database. Finally, Python offers machine learning support.
Firebase – Backend NoSQL database
Setting the starting point as a vertex of a triangle, I could triangulate the area with a side of length total distance / 3, then binding the second and third vertices to the nearest street intersection.
The algorithm works nicely with the APIs I’m using, since this can easily generalize to more any regular polygon to produce a circular path.
iOS Development Challenges
I greatly improved my skills along the way, from MVC patterns, protocols, callbacks to general Xcode expertise.
Since this was my first major non-school software project, I also learned many Developer 101 lessons, like decomposition, consistent code patterns and documentation reading ability.
My initial design was to have two MapViews, the first being an embedded MapController on top of the input box for distance and time.
…and the second being fullscreen with a location beacon that generates an illustrated route when pressed.
However, testing this with friends showed it to be counterintuitive, as many of them didn’t know / had difficulty realizing to click the running man icon.
After a painful farewell to the code for the first embedded MapView, I combined the two into a single full-screen MapView with the distance input at the top.
Going from screen to screen, I realized the UX was still finicky. After all, potential runner converts have low patience level (this was my experience with UnderArmor’s MapMyRun), so I wanted to minimize the number of clicks from opening the app to hitting the ground running.
To do so, I added an onFinishEditing feature that directly triggers the call to the route generation backend once the user finishes entering the distance.
A New Algorithm
As I mentioned, the current algorithm is very rudimentary. Although it gives a decent variety of routes in metropolitan areas, the route works crudely in more rural places. A new graph search algorithm I’m working on is by:
- Retrieving all street intersections within a nearby radius into a graph
- Generating all closed walks of a certain length
- Refining the set to only those within small error of the desired distance, that are randomly given to the user, each time with the option to generate a new one
- Once enough data on which routes the user has chosen or passed up on, training a linear regression model with features as the roads the route passes through of the graph