RateChecker

A SwiftUI iOS app that benchmarks your savings against live Treasury rates, forecasts where rates are headed with on-device Core ML, and shows exactly how much your current bank is costing you.


Unique Value: Most savings apps show you what you have. RateChecker shows you what you're missing — and what to do about it.

Problem

Most people keep money in a savings account without knowing whether it's earning a competitive rate. The gap between a 0.5% APY account and a 5%+ HYSA on $10,000 is $450 per year — silently lost. RateChecker was built to make that cost visible, pull in live Treasury and Fed data, and give users a clear path to better options — all without leaving the app.

Challenge-Based Learning

Challenge: Integrate live financial data from two government APIs, train an on-device Core ML model for rate forecasting, and surface actionable insights without overwhelming users new to personal finance.
Approach: Built a two-tab architecture separating personal finance ("My Money") from market intelligence ("Rates"), used a reusable InfoButton component to teach financial terms in context instead of a separate glossary, and offloaded rate forecasting entirely to a Core ML model running on device.
Outcome: A full-featured iOS app that combines real-time data, machine learning, background monitoring, and financial education in a single coherent experience.

Project Snapshot

  • Platform: iOS (SwiftUI)
  • Stack: SwiftUI · SwiftData · Core ML · WidgetKit · FRED API · US Treasury API
  • Focus: Live financial data, on-device ML, background monitoring, financial education UX
  • Team: Solo
  • Role: iOS developer, ML integration, product design
  • Timeline: April 2026

Key Features

  • My Money Dashboard — add real savings accounts, see your blended APY, projected annual earnings, and the gap vs the T-Bill benchmark
  • Savings Calculator — compare HYSA, CD, Money Market, and Bond Fund returns side-by-side with user-editable APY rates
  • Better Options — curated top HYSAs and CDs from Wealthfront, Marcus, Ally, Discover, Fidelity, and Bread Financial
  • Live Treasury Rates — T-Bills, T-Notes, T-Bonds, and TIPS from the US Treasury Fiscal Data API (no API key required)
  • Economic Context — current inflation rate and Fed Funds rate from the FRED API
  • Core ML Forecast — on-device model predicts T-Bill rates using 14 economic indicators
  • Calculation History — all calculations saved with SwiftData for later review
  • Home Screen Widget — live T-Bill rate always visible via WidgetKit
  • Background Rate Monitoring — checks rates every 4 hours, sends local notifications when rates shift significantly
  • Contextual InfoButton Glossary — every financial term has an inline info button opening a plain-English definition sheet, no separate tab needed
View on TestFlight View on GitHub
Solo project · April 2026 · SwiftUI · Core ML · WidgetKit · iOS

Tech Stack

LayerTechnology
UISwiftUI — @State, @Query, @Environment, @AppStorage
PersistenceSwiftData — SavedCalculation, DepositSession, UserAccount models
Machine LearningCore ML — on-device T-Bill rate forecasting (14 economic indicators)
WidgetWidgetKit — home screen T-Bill rate widget
BackgroundBGTaskScheduler, UNUserNotificationCenter
NetworkingAsync/await — no Combine
APIsUS Treasury Fiscal Data API, FRED API
PatternMVVM — SavingsCalculatorViewModel, HistoryViewModel

Architecture

The app is split into two tabs to separate personal finance from market intelligence:

  • My Money tab — dashboard (blended rate, gap vs benchmark, annual earnings), savings calculator, and user account management
  • Rates tab — live Treasury rates, economic context (inflation + Fed Funds), Core ML forecast, and rate comparison chart

Supporting services:

  • RateService + RateCache — handles all network requests with offline fallback
  • NotificationService — manages rate change and inflation alerts
  • BackgroundRateChecker — enum handling the full BGAppRefreshTask lifecycle
  • InfoButton — reusable component used across all views to surface plain-English financial term definitions

On-Device Rate Forecasting with Core ML

Instead of hitting a server for predictions, RateChecker runs a trained Core ML model directly on device. The model takes 14 economic indicators as input — including current T-Bill rate, inflation, Fed Funds rate, and yield curve data — and outputs a forecasted T-Bill rate. This means predictions work offline and add zero server cost.

CORE ML FORECAST — RateService.swift On-device prediction using trained model with 14 economic indicators.
func forecastTBillRate(indicators: EconomicIndicators) -> Double? {
    guard let model = try? TBillForecastModel(configuration: MLModelConfiguration()) else {
        return nil
    }
    let input = TBillForecastModelInput(
        currentRate: indicators.tBillRate,
        inflationRate: indicators.cpi,
        fedFundsRate: indicators.fedFunds,
        yieldSpread: indicators.yieldSpread
        // ... 10 additional indicators
    )
    let output = try? model.prediction(input: input)
    return output?.forecastedRate
}

Contextual InfoButton — Financial Education Without a Glossary Tab

A key UX decision: instead of sending users to a separate glossary, every financial term (APY, T-Bill, Principal, Fed Funds Rate, etc.) has a small inline InfoButton. Tapping it opens a sheet with a plain-English definition. This keeps education in context — users learn the term at the exact moment they encounter it.

INFOBUTTON COMPONENT Reusable component used across all views for contextual definitions.
struct InfoButton: View {
    let term: String
    let definition: String
    @State private var showSheet = false

    var body: some View {
        Button {
            showSheet = true
        } label: {
            Image(systemName: "info.circle")
                .foregroundStyle(.secondary)
        }
        .sheet(isPresented: $showSheet) {
            TermDefinitionSheet(term: term, definition: definition)
        }
    }
}

// Usage anywhere in the app:
HStack {
    Text("APY")
    InfoButton(term: "APY", definition: "Annual Percentage Yield — the real rate of return on your savings, including compound interest over one year.")
}

Background Rate Monitoring

RateChecker checks rates every 4 hours using BGTaskScheduler — even when the app is closed. If a rate changes significantly, the app fires a local notification via UNUserNotificationCenter. The BackgroundRateChecker enum manages the full task lifecycle: registering, scheduling, executing, and re-scheduling after each run.

BACKGROUND TASK — BackgroundRateChecker.swift BGAppRefreshTask lifecycle: fetch rates, compare, notify if changed.
enum BackgroundRateChecker {
    static let taskIdentifier = "com.geoclink.ratechecker.refresh"

    static func scheduleRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: taskIdentifier)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 4 * 3600)
        try? BGTaskScheduler.shared.submit(request)
    }

    static func handleRefresh(task: BGAppRefreshTask) {
        scheduleRefresh() // re-schedule immediately
        Task {
            let newRate = await RateService.shared.fetchCurrentTBillRate()
            if RateCache.shared.hasSignificantChange(newRate) {
                NotificationService.shared.sendRateChangeAlert(rate: newRate)
            }
            task.setTaskCompleted(success: true)
        }
    }
}

Key Decisions

  • Used the US Treasury Fiscal Data API (no key required) for live rates — lower friction, no credential management in the app
  • Chose SwiftData over Core Data for persistence — cleaner Swift-native API that pairs naturally with @Query in SwiftUI
  • Ran ML forecasting on-device with Core ML rather than a hosted model — zero server cost, works offline, no latency
  • Built the InfoButton as a reusable component rather than a glossary screen — keeps financial education in context where users actually need it
  • Used async/await throughout with no Combine — simpler to read, easier to maintain, fits modern Swift concurrency
  • Two-tab design cleanly separates personal finance ("My Money") from market data ("Rates") so neither area feels cluttered

Outcome

RateChecker demonstrates a full production-quality iOS app: live API integration, on-device machine learning, background task scheduling, local notifications, WidgetKit, SwiftData persistence, and a purposeful UX pattern (InfoButton) that teaches financial literacy without interrupting the flow. The app directly addresses a real financial problem most people don't know they have.

What I Learned

  • Integrating Core ML into a SwiftUI app with a trained regression model
  • Building and distributing a WidgetKit extension
  • Managing background task lifecycle with BGTaskScheduler
  • SwiftData modeling with relationships and @Query-driven views
  • Consuming two government REST APIs (Treasury + FRED) with async/await
  • Designing financial UX for users who aren't financial experts

Next Iteration

  • App Store submission
  • Retrain Core ML model with more historical data for improved forecast accuracy
  • iPad and macOS (Catalyst) support
  • Expand "Better Options" with live rate updates pulled directly from institution APIs