Streaks

Role
Design & engineering (solo)
Year
2026
Stack
SwiftUI · Firestore · Functions · FCM

Streaks started with a simple question: what if a habit tracker asked for proof instead of a checkmark? A photo does more than confirm that something happened. It lets you see the habit changing over time.

The first build was tiny: create a tracker, post one photo a day, keep the streak alive. Then I put it in front of friends, watched the model break, and rebuilt the product around the way people expected it to work.

Starting point

Replacing the checkmark

The first version was deliberately narrow: a personal tracker where the main action was taking a photo. The streak still mattered, but the archive mattered more. It turned progress into something visible.

That changed the product questions. Posting had to feel fast, the archive had to be worth returning to, and the app had to leave room for different kinds of pressure: a visual log, a location boundary, or friends showing up beside you.

Version one
First welcome screen
First welcome screen
Early calendar view
Early calendar view
Early tracker archive
Early tracker archive
Location gating

For habits tied to a place, Streaks can require the user to be there before logging progress. It turns intention into a small boundary: gym trackers at the gym, studio trackers at the studio, and fewer fake wins.

Searching for a required place
Searching for a required place
Setting the boundary
Setting the boundary
Blocked outside the location
Blocked outside the location
Shared tracker flow

Once photos became the core action, shared habits started to feel natural. A workout tracker could become a small group space where friends post progress into the same streak instead of maintaining separate counters.

That meant adding invites, members, shared progress, and enough restraint to keep the group alive without making it noisy.

Shared tracker overview
Shared tracker overview
Finding people to invite
Finding people to invite
Selected friends
Selected friends
Invite link and QR code
Invite link and QR code
People tab
People tab
Member actions
Member actions
Tracker settings
Tracker settings
Group highlights
Group highlights
Shared tracker overview
Shared tracker overview
Finding people to invite
Finding people to invite
Selected friends
Selected friends
Invite link and QR code
Invite link and QR code
People tab
People tab
Member actions
Member actions
Tracker settings
Tracker settings
Group highlights
Group highlights
First users

The early app had two modes: local trackers for quick solo use, and shared trackers that required an account. It looked like a good tradeoff until people started using it.

A few friends created local trackers, deleted the app, reinstalled it, and expected their progress to come back. Technically, nothing could be restored because there was no account to reconnect the data to. Emotionally, the app had lost something they cared about.

That made the lesson clear: continuity is not an advanced feature in a habit app. It is the product.

Model shift

The restore problem pushed Streaks away from local trackers and toward account-backed continuity.

Old model

  1. Create trackerNo sign-in required
  2. Save locallyOnly inside app storage
  3. Delete appLocal copy is wiped
  4. No restoreNo account ever existed

New model

  1. Sign in with phoneCreates a permanent user id
  2. Create trackerTied to that account
  3. Reinstall appSign in again
  4. Progress restoredSynced from the cloud
Landing page phases

The landing page changed as the app changed: from a simple App Store page to a more visual surface that carried the feeling of Streaks.

Rebuild

Making every tracker durable

I rebuilt the product around continuity. Streaks moved from a local/shared split to an online-first model where every tracker belongs to an account. Shared now simply means more than one member, not a separate kind of tracker.

I also simplified onboarding. The old flow asked for email sign-in, then a phone number. The new flow starts with phone verification, then name, username, and account creation. It is less anonymous at the start, but much safer for a product built around history.

The backend had to support that calmness too: moderation before shared photos appear, debounced notifications, silent sync for shared trackers, and retries when uploads arrive before their Firestore records. The current product is still photo-first, but the system around it is clearer, more durable, and easier to keep extending.

Product showcase
Current tracker view
Current tracker view
Photo cutouts
Share card
Share card
Image tap ripple
Lock Screen recap
Lock Screen recap
Invite milestone
Phone-first onboarding
Phone-first onboarding
Tracker archive export
Tracker archive export
Compact widget
Compact widget
QR contact card
QR contact card
Recap reel
Recap reel
Current tracker view
Current tracker view
Photo cutouts
Share card
Share card
Image tap ripple
Lock Screen recap
Lock Screen recap
Invite milestone
Phone-first onboarding
Phone-first onboarding
Tracker archive export
Tracker archive export
Compact widget
Compact widget
QR contact card
QR contact card
Recap reel
Recap reel

Links & credits