Skip to content

Repository files navigation

Elixir: Daily Ritual app icon — a potion bottle, a pocket watch and a shield

Elixir: Daily Ritual

Take your pills. Drink your water. Level up.

iOS 26+ SwiftUI SwiftData, on-device 8 themes plus a custom theme builder Widget: 3 sizes by 3 modes No account required The hydration orb filling from 0% to 63% as a sip, then 500ml, then 750ml are logged

What it does

A medication tracker and a water tracker, in one app, that treats both as the same thing: a ritual you either kept today or didn't. Add what you take, when you take it, how much water you're aiming for. The dashboard is one screen telling you what's left.

Every dose you log is 30 XP. Every glass of water is 10, and closing out your daily goal is another 50. Clear everything scheduled for a day — pills and water — and that's a Perfect Day, worth 100 on its own.

The part that actually keeps you honest is the streak multiplier, because it isn't a badge, it's arithmetic. Three days running puts every point you earn on a 1.1× Bronze Flame. A week gets you Silver at 1.3×. Two weeks, Gold at 1.6×. A month, and Diamond doubles everything you log. Miss a day and the multiplier goes back to 1.0× with the whole ladder to climb again.

Opening Add Ritual and typing a medication name
Name it
Anything you take on a
schedule — pills, drops,
puffs, sprays.
Choosing an icon and a color for the ritual
Give it a face
An icon and a color, so
you recognize the row
without reading it.
Choosing Every Other Day, a start day and a time, then saving
Schedule it
Daily, twice, three or four
times, every other day,
weekly, specific days,
or only as needed.
The dosage unit menu, from mg through IU to spray(s)

Dosage isn't a free-text field pretending to be structured. It's a number and one of thirteen units — mg, g, mcg, ml, L, tbsp, tsp, drops, pill(s), IU, capsule(s), spray(s), puff(s) — so 1 pill and 1000 IU are both sayable without anyone inventing a format.

Why it's good

Switching from the RPG realm to Cyberpunk to Clean; the entire app repaints, and the theme swatches change with it

A theme rewrites more than the colors. There are eight, in three realms — Paladin, Necromancer, Blood Mage and Fae Woods; Neon City and The Construct; System and Lilac Dream — plus a builder for your own. Switching to Cyberpunk sets the font to Courier, turns the "taken" checkmark into a CPU glyph, and renames your rank from Initiate to Script Kid. It even rewrites your reminders: the RPG realm tells you it's time to consume your potion, Cyberpunk tells you your biometric levels are low and to inject immediately.

Progress doesn't become impossible. Levels 2 through 10 cost 150 XP each, which is a fast first week. Level 11 onward costs a flat 1,500 XP, forever — no exponent, no wall you hit in month three. Each realm has its own ten-rank ladder to climb, ending at Legendary Ritualist, The Singularity or plain Legend depending on where you live.

Your reminders don't quietly die. iOS lets an app hold 64 pending local notifications and silently discards anything past that, which is how a reminder app goes mute after two weeks without anyone noticing. Elixir budgets them — up to 40 for medication, 10 for water, 2 held back for a failsafe — and an every-other-day ritual books 7 occurrences covering two weeks instead of burning 60 slots on a year of them. Four separate things top the queue back up: every app launch, a daily background refresh, a weekly deep clean, and, if all three have failed for days, a notification whose only job is to ask you to tap it. There's a screen that shows you the count.

Log a dose without opening anything. Taken and Skip are buttons on the notification itself.

The Reminder Budget screen reading 0 of 64 reminders active, then the dashboard mode picker and the water reminder schedule

One widget, nine layouts. Small, medium and large, each in medication mode, water mode, or both — long-press to pick. The mascot is a potion bottle whose face and fill level track the day: full and grinning when you're done, half full and waiting when something's due, nearly empty and furious when you're overdue. It's drawn entirely from SwiftUI shapes, so there isn't an image asset in the widget bundle.

Water only, pills only, or both. Set the dashboard mode and the app commits to it — Perfect Days and your consistency percentage are computed against whichever halves you actually asked for, not against a goal you never set.

The dashboard showing today's ritual, the week strip and the hydration bar
The dashboard
Today's rituals, the
week behind you, and
how much water is left.
Scrolling up to the Ritual Master profile showing Level 1 Initiate and four stat cards
Ritual Master
Rank, XP bar, both
streaks, Perfect Days.
Every number has a
tap-to-explain sheet.
The Water Ritual History sheet with a weekly bar chart and a per-day log
Water history
A week of intake as a
chart, then every entry
of every day under it.
The All Rituals list showing a saved ritual, and a swipe revealing the delete action
All rituals
Everything you've set
up, with its frequency
and times. Swipe
to remove.

Questions

Will it tell me what to take, or warn me about interactions?

No, and it doesn't try. Elixir doesn't know what your medication is — the name is a string you typed. It's a list, a clock and a scoreboard. Anything about dosage or interactions is a conversation with a pharmacist, not an app.

What happens if I don't open the app for two weeks?

Your reminders keep firing. The queue is refilled by a daily background refresh and a weekly deep clean that iOS runs while your phone is idle, and every schedule is written to cover about two weeks ahead. If the system never gets around to running either — it's allowed not to — you get one notification asking you to tap it, which reschedules everything on the spot.

Do I need an account?

There isn't one to make. No sign-in, no email, no server. Everything lives in a SwiftData store on your phone, CloudKit explicitly off. Nothing in the app makes a network request.

Can I use it just for water?

Yes — Settings → Dashboard Rituals → Water Only, and the medication half disappears from the dashboard and from your stats. Medication Only works the same way in the other direction.

Can I turn the game part off?

Not entirely — XP and levels are always running. But the Clean realm drops the fantasy vocabulary for a plain iOS look and a rank ladder that reads Novice, Believer, Achiever, Consistent, which is about as quiet as a habit tracker gets while still counting something.

Does the widget need the app to be running?

No, and it never reads the database. The app writes a seven-day snapshot of your doses into a shared container; the widget reads that JSON and nothing else. Its timeline is precomputed with an entry at every upcoming dose time and at the fifteen-minute grace period after it, so the bottle's face changes on schedule with the app long since killed.

Is my data backed up anywhere?

Only wherever your iPhone backup goes. There's no iCloud sync between devices yet and no export — delete the app and the history goes with it.

Under the hood

SwiftUI and SwiftData throughout, @Observable view models, a widget extension sharing state over an App Group, and Swift Charts for the water history.

The parts that were actually interesting to build:

  • The widget and the app never share a type, on purpose. WidgetDataTypes.swift in the app and WidgetModels.swift in the extension declare the same structs twice; JSON is the interchange format, so the two processes only have to agree on field names. It's why the widget can't accidentally pull SwiftData into its memory budget, and why the reader tolerates an older snapshot shape rather than crashing on one.
  • Notification scheduling is a budget problem, not a scheduling problem. The hard cap is 64 pending requests. NotificationBudgetManager allocates against it per category and refuses over-budget writes; BackgroundTaskManager runs the four-layer refresh; and every-other-day medications got a specific fix, from ~60 slots down to 7. BACKGROUND_TASKS_SETUP.md has the whole failure-mode table.
  • Themes are a protocol with defaults, not a switch statement. ThemeProtocol supplies the rank ladder, symbol set, font resolution and notification copy from the theme's category, so a new realm is one struct with eight colors and nothing at the call sites changes. The widget gets the active theme's accent hexes inside the snapshot, which is the only way it can match an app it can't ask.

Run it

open ElixirDemo.xcodeproj      # Xcode with the iOS 26 SDK

Build the ElixirDemo scheme and run. First launch is an empty database and a Level 1 user; for populated SwiftUI previews, DataController.preview seeds three medications, four dose logs for today, and a profile sitting on 450 XP with a seven-day streak.

The widget needs the group.com.walhallaa.ElixirDemo App Group on both targets and your own signing team — without the group, the extension falls back to placeholder data and says so in the console.

One wart: the app target links ConvexMobile (convex-swift 0.8.1) and no file imports it. It's dead weight on the build; dropping the package reference breaks nothing.

License and contact

No license file yet, which means all rights reserved by default. Bug reports and feature requests go in Issues — especially reminders that didn't fire, a widget stuck on a stale state, or a streak the app counted differently than you did.


Built by Murat Can Koç

About

iOS medication and hydration tracker with RPG progression — XP with a streak multiplier up to 2x, eight swappable theme realms that rewrite ranks, symbols and notification copy, and a home-screen widget in nine layouts. SwiftUI, SwiftData, on-device only.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages