JunSoft
SpendPulse
Business

SpendPulse

On-device insights into what your spending says about you

Overview

SpendPulse is an on-device spending-insights app that analyzes transactions to surface category, time-of-day, and mood correlations. All financial data is stored locally with SwiftData and never leaves the device. Insights are produced by statistical, deterministic engines (StatisticalInsightGenerator, KeywordCategoryClassifier, SubscriptionAuditor) with no LLM and no network, then visualized with Swift Charts across category, weekday, hour, and mood breakdowns plus a recurring/duplicate subscription auditor. Pro features unlock via a StoreKit 2 subscription, and a Face ID app lock protects the data.

The Problem

Conventional budgeting apps ship data to servers, raising privacy concerns, and only show totals without explaining the 'why' behind spending. SpendPulse keeps data entirely on-device while analyzing mood and time patterns to deliver non-judgmental insights.

Screenshots

App preview

  • SpendPulse screenshot 1
  • SpendPulse screenshot 2
  • SpendPulse screenshot 3
  • SpendPulse screenshot 4
Key Features

What it does

01

Mood-Spend Correlation

Tag each transaction with one of five moods (happy, neutral, stressed, tired, celebratory); PatternAnalyzer.moodAverages computes per-mood average spend. An insight surfaces only when the highest-to-lowest mood ratio is 1.5x or greater, filtering out noise.

02

Time & Weekday Patterns

Buckets weekday spend totals and a 24-hour transaction distribution to find peak days and times. Statistical thresholds gate the output: a weekday peak must exceed 1.8x the non-zero day average, and an hourly peak needs 4+ total transactions with one slot holding over 30% of them.

03

Subscription Auditor

SubscriptionAuditor groups transactions by merchant, currency, and amount, then auto-classifies a group as recurring when the average gap falls into weekly (6–10 days), monthly (26–35 days), or yearly (350–380 days) tolerances. Same-price, same-currency subscriptions across different merchants are bucketed separately to flag duplicate spend.

04

Keyword Auto-Categorization

KeywordCategoryClassifier lowercases the memo and matches a bilingual Korean/English keyword dictionary (e.g. Starbucks, Netflix, taxi) by longest-match to assign one of 13 spend categories. Overlapping rules are safe to add, and because it is deterministic it runs offline and is fully testable.

05

Weekly Deterministic Insights

StatisticalInsightGenerator derives a dominant category (50%+), mood skew, peak weekday, peak hour, and a weekly highlight from a week of transactions as pure functions, capping at five cards. Every rule runs on numbers alone — no LLM, no network — and doubles as the test oracle.

06

Native Chart Dashboard

Visualizes a category donut, weekly trend, mood heatmap, and weekday/time patterns with Swift Charts. The 13 categories map to a color-blind-safe palette that avoids red for spending, keeping the dashboard readable and accessible.

07

StoreKit 2 Subscription

A StoreKitPurchasing actor buys monthly/annual Pro plans through the real payment sheet, verifying VerificationResult, checking entitlements via currentEntitlements, and restoring with AppStore.sync(). It replaces the prior flag-flipping fake purchase path that triggered the 2.1(b) rejection with genuine StoreKit calls.

08

Face ID Privacy Lock

A LocalAuthentication-backed LocalAuthBiometricService gates app entry with Face ID to protect spending data. It checks biometric availability and distinguishes user/system cancellation from real errors, with a mock implementation used to test the auth flow.

Tech Stack

How it's built

Language

Swift 6Swift Concurrency (actor, async/await, AsyncStream)

UI

SwiftUISwift ChartsObservation (@Observable)

Data

SwiftData (@Model, on-device only)Repository pattern

Payments

StoreKit 2StoreKitTest (SKTestSession)

Privacy & OS

LocalAuthentication (Face ID)App Intents (Siri Shortcuts)

Testing

Swift Testing (@Test / @Suite)In-memory ModelConfiguration

Tooling

XcodeGen
Architecture

MVVM + Clean Architecture separating Domain/Data/Presentation, with all analysis built as deterministic pure functions so the app runs 100% on-device with no LLM and no server.

  1. 1

    Repository pattern: the Domain layer depends only on TransactionRepository/GoalRepository protocols and the CategoryClassifier/InsightGenerator abstractions, isolating the SwiftData implementation (SwiftDataTransactionRepository) in the Data layer so storage is swappable and mockable.

  2. 2

    Deterministic insight engine: StatisticalInsightGenerator, KeywordCategoryClassifier, SubscriptionAuditor, and PatternAnalyzer are I/O-free pure functions that run without LLM or network, and the same code doubles as the test oracle.

  3. 3

    Immutable domain model: Transaction is a value type with only let properties, and its validated(...) factory rejects non-positive amounts, empty currencies, and future dates to block invalid data at the boundary.

  4. 4

    StoreKit 2 actor design: StoreKitPurchasing is an actor that caches and reuses loaded Products (avoiding a refetch failure after the price is shown), verifies VerificationResult, checks entitlements via currentEntitlements, and syncs state through the Transaction.updates stream.

  5. 5

    Reactive subscription state: an @Observable SubscriptionStore takes an injected payment engine, subscribes to the entitlementUpdates() stream, and refreshes currentTier on entitlement changes so the SwiftUI paywall and gated UI update automatically.

  6. 6

    On-device privacy: financial data lives only in SwiftData (@Model TransactionModel/GoalModel) with no server dependency, protected further by a LocalAuthentication-based Face ID lock.

  7. 7

    Siri integration: App Intents (LogTransactionIntent, TodaySpendingIntent) plus an AppShortcutsProvider let users log a transaction and check today's spending from Siri and Shortcuts.

By the Numbers

146

Test cases (@Test)

26

Test suites (@Suite)

13

Spend categories

5

Mood tags

6

Supported currencies

100% on-device

Data storage