Case study · iOS

Sipp N Sipp

A water tracker where the point isn't the water. It's that your friends can see whether you drank it.

72% of 2,500 ml

What it is

Three buttons sized to your own bottles. Tap one, the vessel fills, and everyone you've added sees it happen.

Hydration apps are easy to abandon because nothing is at stake. Sipp adds the one thing that keeps a habit alive: other people watching. Friends connect by privately swapping a code, and the leaderboard ranks by how close each person got to their own goal, so someone chasing 4,000 ml never beats someone chasing 2,000 just by being thirstier.

In the app

Dark, quiet, one tap deep

The Today screen, showing a cyan orb filled with water above quick-add buttons
TodayThe vessel is the interface. Quick-add amounts are yours to set, because nobody's bottle is 250 ml.
The Friends screen, showing an invite code, a leaderboard and a live activity feed
FriendsAn invite code, a leaderboard scored against each person's own goal, and a live feed.
The Settings screen, showing goal, units, quick-add amounts and vessel shape options
SettingsGoal, units, amounts, and a choice of vessel: orb, bottle or glass.

Engineering

The parts that were actually hard

Sync

A tap can't wait for the network

Logging water had to work on the metro with no signal, so every entry is written locally first and pushed later. The naive version of that loses data: a tap that lands while a sync is in flight isn't in the response coming back, so overwriting local state with the server's view silently throws it away.

The fix was a push, then pull, then merge cycle where anything still unsynced is carried forward rather than replaced.

Security

Friends see exactly what they're allowed to

Access rules live in the database, not in the app. A stranger can't read your profile or your drinks, or even confirm your account exists, no matter what the app asks for, because the database refuses rather than the app politely declining to show it.

Product

The day starts at 4am

A drink at 1am belongs to the night that's still going, not to a new day nobody has woken up for. Totals, streaks, history and the leaderboard all run on that boundary.

It sounds like a detail until you get it wrong and someone's streak breaks while they're still awake.

Motion

The water is drawn, not played

The fill isn't a video. Two waves are recalculated every frame and clipped to whichever vessel you picked, so the surface keeps moving smoothly even while the rest of the app is busy.

Passing your goal spills water over the rim and pools underneath, then settles to a trickle after a few seconds, because a permanent waterfall is a battery bill that stops meaning anything.

Shipping

Getting it past Apple

Plenty of people can get an app running on their own phone. The distance between that and a listing in the App Store is where most of them stop, and it's most of what this project taught.

Sign in with Apple

Required once you offer any other login. Apple sends your name exactly once, on the first sign-in, so it has to be caught then or never.

Account deletion, in app

A hard requirement. Deleting your account removes it and every drink it owns, immediately, not by email request.

Review, and a rejection

The first submission came back because the reviewer couldn't get in. Fixed with a ready-made demo account that already had a friend, so the leaderboard wasn't empty.

Everything else

Privacy policy, export compliance, age rating, EU trader status, TestFlight, and screenshots for every screen size Apple asks for.

Built with

Stack

App

  • React Native · Expo
  • TypeScript
  • Expo Router
  • Reanimated · SVG

Backend

  • Supabase · Postgres
  • Row-level security
  • Realtime subscriptions
  • Edge functions

Identity

  • Sign in with Apple
  • Email + confirmation
  • Password reset by link

Delivery

  • EAS Build & Submit
  • TestFlight
  • Local notifications
  • Offline-first sync