The Indie Sticker Pack

WWDC is next week, and this year it will be an online experience for obvious reasons. That has not stopped our wonderful community to creatively come together and maintain some of the annual traditions. I am really excited that this week has already kicked off with two such efforts that Tempo is part of.

We opened up sign up for WWDC Virtual 5K yesterday, and today the indie community launched the Indie Sticker Pack! ❤️

You can learn all about the virtual 5K here and here. What I want to highlight in this post is the concerted effort that started from a simple tweet and brought the indie community together to create this epic sticker pack.

  • The initial inspiration came from Becky Hansmeyer’s tweet and it instantly caught many indie developers’ attention.

  • Sam Henri Gold and Nathan Lawrence quickly brought us all together on slack.

  • This was less than a month ago, late on May 22, and everyone was quick to get their icons ready within a couple of days for Sam to get going.

  • Sam owned and facilitated all the big moving parts, with so many details,

    • Logistics around what it would take to put 100s of stickers, from 90+ developers, into a package, and ship them around the world. This is challenging at so many different levels — cost of shipping, cost of printing, best color profiles to use for printing vs digital copies, size of icons to fit everything into a reasonable package, accommodate different icons, layout, and on and on.

    • Get a website up and running for everyone to be able to place orders.

    • I am just amazed by everything that was done in such a short span.

  • The same stickers (plus some more 😃) are also available as an iMessage sticker pack. This was made possible with George and Gianpiero’s help.

  • To take the inspiration further, Charlie Chapman made an amazing video that I have watched over a dozen times. 🤩

It’s an epic book of indie delight 😍

These stickers are beautiful. I still haven't ordered mine, because I can't settle on how many 🙃. But they are gorgeous, I love the sheets format. When you order, you won’t get one, two, but 10 sheets of awesomeness!!!

This is rare, and I want to preserve it as the legacy of the indie community during a critical year of our lives. It’s a byproduct of the creative collaboration of a bunch of independent developers. Most of us work solo, on our own, or in very small teams. It was inspiring to witness all the artists go at it together, moving at a blazing pace. And none of it would be possible without Sam!

2 Worthy Causes ❤️

If I may, let me just say one more thing — it is only $12.99 for the entire package, and all your money goes towards two great causes,

I feel really honored and proud to be part of such a creative, compassionate, and wonderful community. And I hope you will join us by buying a few of these sticker packs.

Introducing Activity Stats

Last Wednesday, April 29, 2020, Ian Blackburn tweeted a screenshot of his Total Apple Watch Activity numbers. He calculated the totals on a spreadsheet, by exporting the data from the Health app on his iPhone.

Ian’s data made me curious about my totals and it seemed really easy to put together an app for it. And so I started building Activity Stats. With all the work I have been doing with HealthKit in Tempo, it was really straightforward to get the data out of the Health app. My recent learning/playing with SwiftUI made it extremely fast and easy to put together a nice looking UI.

I shared the app with Ian and few other friends via TestFlight, and everyone found it interesting and thought it looked good. Even my 10 year old wanted it on her iPhone. That seemed adequate amount of validation for me to polish up and launch a quick v1.0 of the app. So today, 7 days after starting it as a brand new project in Xcode, I am excited to be releasing Activity Stats in the App Store! 🚀

Activity Stats is a simple app that displays your lifetime totals of various types of fitness and workout data stored in the Health app on your iPhone. Here's the list of totals it currently supports,

  • Total active calories

  • Total exercise time

  • Total steps count

  • Total walking and running distance

  • Total walking and running distance

  • Total walking distance from workouts

  • Total swimming distance

  • Total cycling distance

  • Total indoor cycling time

  • Total elliptical time

  • Total workout time

  • Total workouts

Activity Stats is available for free in the App Store. If you are curious about your totals, please do check it out.

Introducing Tempo on Apple Watch

It’s finally here — Tempo now works on your Apple Watch!

This is for all of us, who need a little extra motivation to do our runs these days. You can do the following with Tempo on your Apple Watch,

  • Track distance totals for the current week, month, and year.
  • Use a Tempo complication on your favorite watch face to stay motivated towards your week, month, or year's distance goal.

A feature request that could help many runners

For many of us, our goals keep us motivated through our training cycles. In the midst of a global crisis, with races getting cancelled for the current season, that motivation has been a bit lacking. So when Zac, one of Tempo's champion runner and evangelist, reached out with a suggestion about a watch complication to track the current month's total distance, I instantly knew that it was a great time to build and launch Tempo's watch app. It could be a way to participate by enabling runners to maintain some long-term focus during the time of crisis.

From zero to a functional app

The initial app took less than a couple of days to put together. That was followed by fine tuning all the watch face complications, and a lot of field testing to further refine details like the following,

  • How does a Tempo complication update everyday; especially at the beginning of a period — week, month, or year — when the total distance for that period resets. Not tricky in terms of development, but I wouldn't have discovered this scenario without doing adequate real-world testing, over multiple days.
  • How does a Tempo complication update after a run that was tracked using the Workout app on the Apple Watch. I have some hooks implemented to handle this, but don't think it's perfect, and will need more testing and work to fine tune.[1]
  • How to avoid data discrepancy while maintaining bidirectional sync between Tempo on the watch and Tempo on the iPhone. Think transient source of truth and merge conflicts around pre-synced Health data across devices.[2]
  • There are a lot more, behind-the-scenes, dev-level tooling and practices that I identified and put together to be able to do productive development with a tethered watch device. A lot of iterative rewrite/refactoring happened to do things the right way.

Some of the above is challenging to get right because it requires testing on a real watch device with real workout data. Add to that the limitation of the fact that I can only workout (run) once these days. I could have skipped some of it for this initial launch version, but when the data is meant to be available at a quick glance and represents a goal metric, fast data precision and integrity is of utmost importance.

Staying focused during the pandemic

Building this has been a great distraction from all the pandemic news while staying curiously focused through the fragmented workday hours with kids at home. Some key development highlights include,

  • This is my first SwiftUI app in the App Store. SwiftUI is such a great tool for developers (and designers). Thanks to Apple engineers for making UI development so much easier and fun to do. Technically, I really like the immutability and state management mechanism for views in SwiftUI. It also feels a lot faster to prototype and transform into production-ready UI.
  • Developing and testing watchOS app that accesses Health data has it's own peculiarities that were both enjoyable and frustrating to keep me curious and engaged.

Overall, I enjoyed exercising some new development muscles with SwiftUI, WatchKit, WatchConnectivity. I also appreciated how my running while build-testing this transformed from more than the usual fun physical activity to also the need to go get some real-world testing done. It led to a 13-day running streak for me, which might be my longest streak in recent years, and will probably remain that way through 2020.

Sparking inspiration & motivation

The app itself is simple right now, but as Zac put it, "perfect for sparking inspiration and motivation." Having a monthly distance complication on the watch face has been a great motivation to keep running. Seeing it first thing in the morning as I glance for time/date/weather gets me oriented about my day's workout goal. Seeing it later in the day, after a run, is a nice boost to that feeling of accomplishment and staying on track towards a long-term goal. I want to say it’s the equivalent of Activity app for running, but that's too early, and more of an aspirational goal. A lot more to come!

My thanks to Zac for sending this feature request that allowed me to do something via Tempo for our community during these crazy times.

I hope you are still able to run safely by following social distancing measures, and this update helps maintain that spark and joy of running.


  1. Accessing HealthKit data in the background via a complication datasource seems to be a hit or miss right now. I need to do more research and see if I could use pedometer data to bridge the inconsistencies. ↩︎

  2. In general, the watch app relies on the phone app to provide all the data. But when we go for a run without a phone, Tempo on the watch takes over to update the data (that was originally generated on the phone) with the latest workout data generated by the Workout app on the watch. Even when the phone to watch sync is active again, the latest workout data from the watch has to first sync with the phone before that data becomes available to Tempo on the phone. So there is a delay for the phone app to refresh with the latest workout data. This results in the phone app to continue sending the older version of the data (before the latest workout) to the watch app, and watch app continues to merge the recent workout data. At some point, the phone app catches-up, and sends most up-to-date data, and the watch app stops doing the merge. ↩︎

RCA of Subscription Outage in v2.9.0

In the tech world when a software system’s functionality is degraded due to software defects (bugs) and those bugs cause a severe event or outage, we do a root cause analysis (RCA). Tempo’s v2.9.0 caused an outage for paid subscribers, where premium subscription was not recognized by the app, and no one could access premium features. It was a simple programming mistake, but a long time coming, and as it affects my long-term strategy, I want to share the details of this RCA with everyone.

I am sorry!

First, my apologies about the outage. This should not happen, and I will strive to do better next time.

While I wouldn't want this to have happened, the support and kindness that I received from all of you, as we interacted over the emails during the outage, was really encouraging for me. Thank you for your patience and understanding. ♥️

What happened?

Tempo's premium subscription was not recognized by v2.9.0 of the app. Even attempting to restore a previous purchase did not fix the issue. This meant that users, who had subscribed to premium features, were not able to access any premium features i.e. Tempo could only be used in basic free mode.

The issue was discovered late on Wednesday night (Feb 18, 2020). I was able to troubleshoot and fix the bug overnight to submit v2.9.1 with a request for an expedited app review. The App Store team was kind enough to pick it up first thing in the morning (Thursday), and approved it quickly. The outage lasted for ~12 hours.

Why did it happen?

v2.9.0 introduced a software defect caused due to code changes around how subscriptions (free trial and paid) are recognized as well as restored in Tempo.

Why did v2.9.0 change the code that handles subscriptions?

In v2.8.0, Tempo replaced a custom implementation of the free 14-day trial with the App Store's standard free trial implementation that is supported and managed by the App Store's payment system infrastructure.

When a subscription transitions from a free trial to paid (the customer has decided to continue with the subscription), the subscribed app (Tempo) receives a callback from the App Store payment system to notify the app that it should continue to recognize the subscription as active.

In the absence of this callback, the app will consider the free trial to be at the end of the trial period and will disable the subscribed features (Tempo premium in our case). Starting with 2.8.0, some folks reported a delay between the time the free trial ended and the paid subscription became active. In order to resolve this issue, I updated the code in the app that handles subscriptions, and released v2.8.2. But the issue did not completely go away, and I was still hearing about the delay from some folks. So in 2.9.0, I implemented a grace period to keep Tempo premium enabled for a few more days after the subscription expires. With these changes, if the app does not receive an update for the free trial to paid subscription transition in time, this grace period kicks in to prevent any disruption for the folks who have paid. And yes, this also means that folks who do not continue with their subscription will get extra free time of premium, but I would rather allow extra days of free premium for folks who don't pay than have any kind of disruption for all you caring, paying patrons.

Why was the defect caused by a code change not caught during testing, before affecting real customers in production?

I made a human error in my code around date comparison to identify a date in the past. Instead of ordering the dates, my careless code compared the dates by checking for the time difference between the two to be less than 24 hours. This code change was tested with sandbox environment of in-app payment processing services, where test subscriptions expire within 24 hours. This bug was not discovered due to specificity of 24 hours in the sandbox environment and in my code. Also, testing all the subscription scenarios with a sandbox environment require a lot of manual subscribe-wait-test-repeat cycles that can take hours (or a full day at times) and is, therefore, hard to automate.

Why did a defect in processing subscription callbacks break access to premium features for customers who already had active subscriptions?

Tempo derives access to premium based on current subscription state (unsubscribed, active, or expired). This state is updated with payment transactions callbacks. The code change was meant to refine state transitions more gracefully, but in the process it also affected existing (unchanged) state. The change caused a bug that resulted in subscription state for every user to appear expired.

What should be changed to avoid this?

The key issues that caused this outage are,

  • State of a subscription, that acts as a source of truth for access to premium, is coupled with the code that also handles processing transactions. This state management is done on the device and the state itself is mutable — it can change due to code changes.
  • Testing changes to this code is difficult to do in a production environment, before releasing to the App Store.

Tempo currently has no custom server-side services (a backend for Tempo), so all payment transactions are processed on the device and subscription state is also maintained on customer's device. This code is complex, fragile, and doing adequate testing of the app connected to a sandbox environment is challenging.

This needs to change to support following,

  • Separate, immutable source of truth for access to premium. This should be decoupled from the code that processes new transactions.
  • Ability to test code in pre-released versions of the app.
  • Ability to quickly fix any production outages with minimum reliance, none in most cases, on the App Store review team. In other words, while the app review team is awesome, do not rely and abuse the expedited app review requests process.

A better architecture here would be to implement a backend that would maintain source of truth for access to premium features in a database. This source of truth will still be updated by new transactions, but instead of modifying database records, it would maintain a list of updates/additions — similar to App Store API structure for payment transactions. This architecture will then provide following benefits,

  • Enable better testing and provide ability to automate various scenarios.
  • Unlike app-side, server-side source of truth records will be permanent and can not be deleted. This further reduces the risk from future code bugs (as on the app-side) around recreating the source of truth.
  • Handle similar outages much faster — server-side code fixes won't require changes to the Tempo app, avoiding full release + app approval cycle.
  • More graceful handling of interruptions to the production environment of in-app payment processing services.
  • Better security around subscription verifications.

Conclusion

Tempo's primary goal is to be the best running app out there. I have been obsessively focused on building all the features that runners need. It's gratifying to ship all these app-side features for runners (including myself), but in doing so, I haven't been prioritizing upgrading to a desirable server-side architecture for improved resiliency. Tempo started as a side-project, and I have been delaying the server-side part until I got to building some of the features on the roadmap that will require a backend service. But that lack of a backend is now causing disruptions to the quality experience that patrons expect, and I deeply care to deliver. Quality is at risk due to my focus on quantity of app-side features. I have already started looking into building out the backend architecture for Tempo. After the current in-progress projects are shipped, porting over in-app purchase processing to server-side will become my top priority.

My thanks to Eric in helping craft this message to make more sense vs me just rambling.

Tempo v2.8.0

I started this version with the intent to launch a brand new feature in early January 2020, but I didn’t foresee some caveats in the implementation of the new feature, and didn’t want to delay all the work that I am finding extremely pleasant and useful while dogfooding the app everyday. So here we are. The new feature is still happening; just delayed to work out the details. I am also trying an experiment to build and launch another app that might precede this new feature. It’s still very much related to running, but I better 🤫 before sharing too much!

Here’s what we got in this release,

  • Edit total distance of a run

  • Satellite mode for route map

  • Pause indicators for segments and total duration

  • UI Improvements

Edit Total Distance

This is the key feature of this release. It enables ability to edit total distance of a run that has been already saved in the Health app via Apple Watch. The primary idea behind editing total distance is based on 2 different scenarios,

  1. Races: Races have officially measured course, but the actual distance we cover with our watch might be slightly off due to GPS issues or turns. As runners, our races are the highlight events of the season (or otherwise). So our log showing our race performance inaccurately, with incorrect pace, can be annoying. Now we can fix it using Tempo.

  2. Indoor Runs: Indoor runs are tracked based on the treadmill data (or pre-measured laps on an indoor track). Due to lack of GPS, most tracking devices often report these runs inaccurately. But now we have the control to fix the distance.

Tempo will also auto-update the average pace based on tracked duration. At some point in the future, we will add a more elaborate editing / trimming to be able to slice off part of runs for scenarios when we forget to stop the watch. I originally had this feature as part of trimming, but one of the runners reached out asking for a simpler option as implemented here. Instead of waiting too long for an entire super-fancy, but trickier to build, trimming functionality, I decided to build and release this quickly.

The feature itself seems straightforward, but behind the scenes it took some rearrangement of how we access and cache Health data to account for edited total distance. It's always interesting (and scary) how the entire system setup gets affected when we change a read-only UI to support read-write functionality. We have had notes and tags for write functionality, but this is the first time that Tempo is going to support editing running data. And I believe, with this feature, Tempo is the only running app out there to support updates to a run that has been already saved in the Health app from the Apple Watch Workout app.

Please note that none of these changes will delete or edit your Health data. That’s technically not permissible by HealthKit API, and I wouldn’t endanger our running data. When you edit the total distance, Tempo makes a local entry within Tempo (and backed up to your iCloud) for that run along with your edit. The changes are around how Tempo reads and caches data to your iCloud account to include changes to the total distance. Your edits for the total distance are saved to your iCloud account along with your notes and tags.

To edit total distance of a run, scroll all the way to the bottom of details screen of any run and tap Edit Total Distance.

IMG_2BA673F528CF-1.jpeg

Satellite Mode for Route Map

That’s exactly as it sounds — we can now relive any of our runs with real-life satellite imagery of the route. And it can also be shared. 🙌

IMG_0958.JPG

Pause Indicators

If you pause during your runs, you will see the pause times indicated in more places.

  • Splits for segments (released in v2.7.0) now include pause times.

  • Total duration of every run now shows total run time + any paused time (in total).

UI Improvements

As soon as you download 2.8.0, you will notice the general screen flow and transitions are slightly different in a good way. These changes are more aligned with overall iOS style, and specifically with iOS 13. In general, you will notice,

  • Run details and screens off of dashboard are now pushed in from right vs vertically from bottom. This should allow for swiping right to go back (dismiss). And it also allows for easily using page sheet modal screens (example: editing total distance of a run) in various parts of the app.

  • Bottom bar (aka tab bar) auto-hides and pops back up when the above screens are pushed in / out.

These changes were kind of long overdue and something that I couldn’t afford to spend time on when Tempo was not my full-time project. Getting better everyday. 😃

As always please reach out with feedback or say hi.

Enjoy & Keep Running!

PS: If you are interested in helping beta test the new app, please email rahul@indie.sh or DM @TempoLog.