An iOS app that helps everyday people explore how savings rates compare to US Treasury rates and inflation. Built with SwiftUI, Swift Charts, WidgetKit, Core ML, and live data from the US Treasury and Federal Reserve APIs. Not financial advice — just data presented in plain English.
Most finance apps assume you already know what a T-Bill is. This one doesn't. I built this to teach myself how savings rates, Treasury rates, and inflation all connect — and to present that data in plain English so anyone can explore it. It's a learning project, not financial advice.
- Calculator — Compare Money Market, CD, and Bond Fund returns side by side for any principal and time horizon
- Visualizer — Swift Charts bar chart of recent T-Bill rates with your rate overlaid as a dashed reference line
- Live Rates — Real-time Treasury Bill rate from the US Treasury API, shows the exact dollar amount you're leaving on the table annually
- Compare — Side-by-side bar chart of T-Bills vs T-Bonds vs T-Notes vs TIPS, with plain English explanations of each
- Inflation Check — FRED API integration shows whether your savings rate is keeping up with CPI inflation
- History — Save and review past calculations with SwiftData persistence
- Glossary — Searchable plain English definitions of financial terms (APY, T-Bill, CD, FDIC, etc.)
- Home Screen Widget — WidgetKit widget showing the current T-Bill rate (small and medium sizes), refreshes every 4 hours
- ML Rate Forecast — On-device Core ML model predicts the next T-Bill rate using 14 live economic indicators (fed funds rate, CPI, PCE, yield curve, unemployment, oil prices, and more). Shows the prediction alongside the actual rate — no advice, just data.
- Offline Support — Cached API data loads instantly, fresh data updates in the background
The project follows MVVM (Model-View-ViewModel) with a clear separation between UI, business logic, and networking.
- Testability — ViewModels contain all business logic and can be unit tested without any UI. The test suite has 50+ tests covering calculations, recommendations, API parsing, mock networking, and edge cases.
- Separation of concerns — Views only handle layout and binding. They don't compute returns, decide which product is best, or interact with SwiftData directly.
- Scalability — Adding the Live Rates and Compare tabs didn't require touching existing ViewModel logic. Each screen owns its data flow.
SavingsCalculator/
├── Models.swift Shared data models (SavedCalculation, DepositSession, SavingsProduct)
├── RateService.swift Networking: Treasury API, FRED API, caching, secrets
├── RateForecastService.swift Core ML prediction wrapper for RateChecker model
├── RateChecker.mlmodel Trained ML model for T-Bill rate estimation
├── SavingsCalculatorViewModel.swift Calculator logic: product comparison, recommendations, save
├── HistoryViewModel.swift History deletion logic
├── SavingsCalculatorApp.swift App entry point + Calculator, ProductCard, History views
├── ContentView.swift TabView container (6 tabs)
├── VisualizerView.swift T-Bill rate chart with Swift Charts
├── LiveRatesView.swift Balance gap calculator with live API data + inflation
├── RateComparisonView.swift Treasury securities comparison with bar chart
├── GlossaryView.swift Searchable glossary of financial terms
├── Secrets.plist API keys (gitignored, never committed)
RateWidget/
├── RateWidget.swift Home screen widget (small + medium)
├── RateWidgetBundle.swift Widget entry point
SavingsCalculatorTests/
├── SavingsCalculatorViewModelTests.swift ViewModel + model unit tests
├── LiveRatesViewTests.swift Rate gap, inflation, and cache tests
├── RateServiceTests.swift Async mock network tests (MockURLProtocol)
├── UITests.swift Integration tests for end-to-end app flows
| Decision | Why |
|---|---|
@Observable ViewModels |
Modern Observation framework — cleaner than ObservableObject/@Published, better performance |
Shared savingsProducts array |
Defined once in Models.swift, used by Calculator and Visualizer — no duplication |
RateCache (UserDefaults) |
Cached API responses load instantly on launch; stale after 4 hours; graceful offline fallback |
Injectable URLSession |
RateService and FREDService accept a session parameter (defaults to .shared) — enables mock network testing without hitting real APIs |
Secrets.plist (gitignored) |
API keys never touch version control; app reads from bundle at runtime |
ContentUnavailableView errors |
Polished error states with retry buttons instead of raw error text |
StaticConfiguration widget |
No user configuration needed — just shows the T-Bill rate |
Swift Charts BarMark + RuleMark |
Visualizer overlays user's rate on top of T-Bill data for instant comparison |
| Accessibility throughout | Every data card and input has accessibilityLabel and accessibilityValue for VoiceOver |
| Core ML on-device inference | RateChecker.mlmodel runs locally — no data leaves the device, no server costs, works offline once inputs are cached |
| 14 FRED series fetched in parallel | async let fires all API calls concurrently for fast model input loading |
| API | What it provides | Key required? |
|---|---|---|
| US Treasury Fiscal Data | T-Bill, T-Bond, T-Note, TIPS rates | No |
| FRED (Federal Reserve) | Fed Funds rate, CPI inflation, PCE, unemployment, 10yr/2yr yields, M2, credit spreads, oil, savings rate, industrial production | Yes (free) |
- Clone the repo and open
SavingsCalculator.xcodeprojin Xcode - (Optional) Get a free FRED API key at fred.stlouisfed.org
- Create
SavingsCalculator/Secrets.plistwith your key:<dict> <key>FRED_API_KEY</key> <string>your_key_here</string> </dict>
- Make sure
Secrets.plistis in Build Phases > Copy Bundle Resources - Build and run on a simulator or device (iOS 17+)
The Treasury API works immediately with no setup. FRED data (inflation, Fed Funds rate) requires the optional key.
Press Cmd + U in Xcode. All tests use the Swift Testing framework (@Suite, @Test, #expect). Tests cover:
- Product return calculations (compound interest, edge cases, zero principal)
- ViewModel defaults, best product selection, recommendation boundaries
- Treasury API JSON decoding (valid, empty, negative rates)
- FRED API observation parsing (valid, missing data placeholders)
- Shared product list validation (ordering, flags, completeness)
- Async mock network tests —
MockURLProtocolinterceptsURLSessionrequests with fake JSON, testing success paths, caching, deduplication, invalid JSON, and network failures without hitting real APIs - Integration tests — End-to-end flows through the model layer: calculator input → recommendation, cache round-trips, CSV export, and product comparison consistency
- Xcode 16+
- iOS 17+
- Swift 5.9+
For informational purposes only. Not financial advice. Rate data sourced from the US Treasury Fiscal Data API and Federal Reserve Economic Data (FRED).