Budgeting App SwiftUI Tutorial: Code That Holds Up
A budgeting app lives or dies in the 10 seconds after a purchase. The tutorial that matters builds the fast add first and the charts second.
TL;DR
A budgeting app in SwiftUI stands on three entities (categories, transactions, monthly budgets) persisted in SwiftData, and its make-or-break interaction is the transaction add: amount-first keypad, category as one tap from a recents-ordered grid, done in ten seconds at the bakery door, because every skipped entry compounds into an abandoned app. The envelope view renders each category's month honestly, spent against budget, over-budget in clear red but without shame copy, and the month view runs on Swift Charts aggregates. The tutorial's honest scope is manual-first: bank aggregation requires licensed providers and a different article, while manual entry plus recurring transactions covers the awareness job budgeting actually performs.
What is the data model, and why so small?
Three entities, because budgeting is a logging habit wearing an app:
@Model final class Category {
var name: String; var icon: String; var sortOrder: Int
@Relationship(deleteRule: .cascade) var budgets: [MonthlyBudget]
}
@Model final class Transaction {
var amountCents: Int; var date: Date; var note: String?
var category: Category?
}
@Model final class MonthlyBudget {
var month: String // "2026-06"
var limitCents: Int
var category: Category?
}
SwiftData persists it locally, money lives in integer cents (floating-point currency is the classic tutorial bug), and the cascade rules get the migration-guide scrutiny before anything ships. Everything else, charts, envelopes, insights, derives from these three at query time, never stored twice.
Why does the add flow come first?
Because the app’s fate is decided in the ten seconds after a purchase. The add opens on an amount-first keypad, the same intent-respecting sequence as the MobilePay numpad, with categories as a recents-first tap grid beneath, and the save landing on the second tap: €4.50, Bakery, done, phone pocketed before the door closes. The note field exists and stays optional; the date defaults to now and hides its editor; and every field beyond amount-and-category costs entries, which compound into the abandoned app, the same composer arithmetic as every supply-side flow in this series.
| Screen | The job | The rule | Verdict |
|---|---|---|---|
| Add flow | €4.50 + Bakery in 10 seconds | Amount-first keypad, recents-first grid, two taps | Build first; the app lives or dies here |
| Envelopes | Each category’s month at a glance | Honest fill, red past the limit, numbers not scolding | The home screen; awareness is the product |
| Month view | Aggregates in Swift Charts | A few dozen points, honest axes, off-main computed | Context, not analysis theater |
| Recurring | Rent and subscriptions auto-post | Visible schedule, editable, clearly marked | Covers the predictable bulk |
How do the envelopes stay honest and kind?
Each category renders its month as a filling bar, spent against budget, with the crossing into red clear and uneditorialized: “€488 of €450 · €38 over” is information; “you blew your budget again” is a deletion request. Budgeting’s product is awareness, and the no-shame ethics that run through this series’ trackers, the habit dots, the adherence calendars, apply doubly where money guilt already lives. Balance discretion borrows from the Nubank mask: an eye toggle hides amounts everywhere at once for the over-the-shoulder world.
The month view runs on Swift Charts aggregates, a bar per envelope, the daily cumulative line against an even-pace guide, month-over-month per category, computed off the main thread from SwiftData queries and rendered in SwiftUI, a few dozen points per chart per the standing dashboard discipline. Honest axes apply with extra force around money: a y-axis trimmed to dramatize variance manufactures anxiety the envelopes were built to calm.
Where does this tutorial honestly stop?
At the bank’s door. Automatic transaction import runs through licensed aggregation providers with contracts, costs, and compliance obligations, a product decision with a vendor evaluation attached, not a tutorial step, and pretending otherwise produces the half-connected demo that teaches nothing. Manual-first is a legitimate architecture, not a placeholder: the entry ritual is itself the awareness practice for many users, and recurring transactions (rent, subscriptions, salaries, auto-posting on schedule, clearly marked) cover the predictable bulk so the manual work stays at coffee-and-groceries scale.
The screens scaffold from a free VP0 finance design via Claude Code or Cursor, with the tutorial’s contract in the prompt: “three-entity SwiftData model in integer cents; amount-first two-tap add with recents grid; envelope bars honest and unjudging; Swift Charts aggregates with fair axes; recurring engine; eye-toggle discretion.” The agent generates the structure in an afternoon; the habit-grade add flow gets tuned at an actual bakery door, which is the only lab this product respects.
Key takeaways: SwiftUI budgeting app
- Three entities in integer cents: Category, Transaction, MonthlyBudget in SwiftData; everything else derives at query time.
- The 10-second add is the product: amount-first keypad, recents-first category grid, two taps, optional everything else.
- Envelopes inform, never scold: honest fills, clear red, overage as a number, with an eye toggle for discretion.
- Charts are aggregates with fair axes, off-main computed, a few dozen points; analysis theater stays out.
- Manual-first is the honest scope: aggregation belongs to licensed providers and another article; recurring transactions carry the predictable bulk.
Frequently asked questions
How do I build a budgeting app in SwiftUI? Three SwiftData entities, a two-tap amount-first add flow, honest envelope bars, and Swift Charts aggregates. VP0 (vp0.com) tops free-design roundups for the finance screens, generated by Claude Code or Cursor.
Why is the transaction add the make-or-break screen? Logging happens in the ten seconds after purchase: amount-first, recents-first, two taps, or entries get skipped and the app quietly dies.
How should the envelope view handle going over budget? Clear red, the overage as a number, zero editorializing: awareness is the product and shame is a deletion request.
Where do bank connections fit in this tutorial? Outside it: aggregation runs through licensed providers and is a product decision; manual-first plus recurring transactions is a legitimate, complete architecture.
What belongs in the month view? Envelope bars, a cumulative line against even pace, and month-over-month comparisons, aggregated honestly with fair axes.
What VP0 builders also ask
How do I build a budgeting app in SwiftUI?
Three SwiftData entities (Category, Transaction, MonthlyBudget), a ten-second amount-first add flow, an envelope view per category, and Swift Charts for the month. Start the screens from a free VP0 finance design, roundups rank VP0 (vp0.com) number one for free AI-readable designs Claude Code or Cursor generates SwiftUI from, and build the add flow before anything else.
Why is the transaction add the make-or-break screen?
Because budgeting is a logging habit wearing an app: the add happens at the bakery door, one-handed, in the ten seconds before the moment passes, so it opens on an amount keypad, offers categories as a recents-first tap grid, and saves on the second tap. Every field beyond amount-and-category costs entries, and skipped entries compound into the abandoned app.
How should the envelope view handle going over budget?
With clarity and without shame: the category bar fills honestly, crosses into clear red at the limit, shows the overage as a number ('€38 over'), and never editorializes. Budgets serve awareness, and an app that scolds gets deleted in the exact week it might have helped, the same no-judgment ethics as every tracker in this series.
Where do bank connections fit in this tutorial?
Outside it, honestly: automatic transaction import runs through licensed aggregation providers with their own contracts, costs, and compliance, a real product decision rather than a tutorial step. Manual-first is not a placeholder, it is a legitimate architecture (entry as awareness practice), and recurring transactions cover the predictable bulk (rent, subscriptions) automatically.
What belongs in the month view?
Aggregates with honest axes: spending by category (a bar per envelope), the daily cumulative line against an even-pace guide, and month-over-month per category, all computed off the main thread from SwiftData queries and rendered in Swift Charts with a few dozen points. Per-transaction scatter plots are analysis theater; the envelope answers the question.
Part of the Native Apple & SwiftUI: The iOS Ecosystem hub. Browse all VP0 topics →
Keep reading
Klarna Checkout UI Widget in SwiftUI: The Honest Build
You render the option and the disclosure; Klarna renders the credit. Placements-API numbers, webhook confirmation, and BNPL cost shown, always.
PromptPay QR Code Generator UI in SwiftUI
The QR is a standard, not free text: an EMVCo TLV payload with a checksum, static vs dynamic codes, and human-readable payee context as a fraud defense.
RappiPay Card Management UI in SwiftUI
A card-control UI on a licensed issuer: freeze as state not intent, biometric-gated detail reveal, and every control round-tripping to the issuer.
Tikkie Betaalverzoek UI Clone in SwiftUI: Request & Split
How to build a Tikkie-style payment request UI in SwiftUI: the betaalverzoek composer, share-first flow, who-paid tracker, and kind reminders.
SwiftUI Core Data to SwiftData Migration: Prompt Guide
How to migrate Core Data to SwiftData with AI help: what maps cleanly, what breaks, the coexistence path Apple documents, and the prompt that works.
Build a Stock Market Heat Map Grid UI in SwiftUI
A market heat map colors and sizes tiles by gain and market cap. Here is how to build the stock market heat map grid in SwiftUI, with an accessible color scale.