Skip to content

WORK//01

MoneyLog

A private, offline expense tracker built with SwiftUI and SwiftData.

Type
iOS app
Platform
iPhone · iOS 18+
Role
Solo: design, architecture, code and tests
Year
2026

Overview

MoneyLog is a personal finance app for iPhone that keeps everything on the device. There is no account to create, no server to talk to and no analytics. You log income, expenses and transfers between accounts, set monthly budgets and savings goals, and see where the money went on a set of Swift Charts.

I built it to practise the parts of iOS development that matter in real products: a data model that can't drift, a clear separation between screens and storage, and tests around the code where being wrong would be expensive.

The problem

Most expense trackers either want your bank login or your email address. I wanted one that works fully offline, opens instantly, and whose totals you can trust to the last paisa: no floating-point drift and no stored balance that can quietly go out of sync.

What I built

  • Today screen with net worth, this month's income and spending, a daily spend chart and a plain-English insight such as “You're ₹1,200 under pace with 9 days to go”
  • Activity history with search and filters by type, category and account
  • Insights: Swift Charts for the selected month, broken down by category
  • Plan: monthly budgets with a pace marker, and savings goals with the monthly amount needed to hit a target date
  • Multiple accounts and transfers between them, which never count as income or spending
  • Recurring transactions, duplicate detection and a recent-merchants list for fast entry
  • Appearance, currency and an erase-everything option in Settings

Architecture & key decisions

Money is stored as whole paise

₹250.75 is saved as the integer 25075 in an Int64, never as a Double. Adding two different currencies stops the app in debug instead of quietly producing nonsense.

Balances are calculated, never saved

An account's balance is its opening amount plus every transaction since. A pure BalanceCalculator works on lightweight LedgerLine values, so it can be tested without a database.

Only repositories touch SwiftData

Screens ask a repository for “recent transactions” or “save this”. Validation lives in one place, and tests swap in an in-memory store.

One @Observable view model per screen

The view describes layout; the view model does the arithmetic. A small AppRouter owns navigation state and a data-version counter that tells every screen to reload after a save.

A real design system

Every colour, font, spacing value, animation timing and haptic comes from DesignSystem tokens. Change Palette.swift and the whole app follows.

Challenges

Comparing half a month with a whole one

“You've spent less than last month” is always true on the 5th. The dashboard compares this month to the same elapsed stretch of last month instead, so the comparison is fair on any day.

Budgets that tell you something useful

A progress bar alone doesn't say whether you're on track. The budget calculator works out where you should be today if you spent evenly, and the gap between that and what you've actually spent drives the insight copy.

Outcome

A codebase a new developer can learn from its README alone. The README explains the five decisions that shape the code and follows one action end to end. Unit tests cover money arithmetic, balance maths, budget pace, recurrence rules and repositories, and a UI test catches launch crashes.