Building an AI-Native iOS Budgeting App in Two Weeks
How I built a production-ready financial management app with Flutter, Firebase, and Claude AI using AI-powered development tools.
Two weeks. That's all it took to build a production-ready iOS budgeting app with AI at its core.
Not a proof of concept. Not a hackathon demo. A fully functional financial management system processing bank emails in real-time, categorizing transactions with Claude AI, sending push notifications to your iPhone lock screen, and learning from your spending patterns—all deployed to production and handling real money.
This isn't about hype. It's about what's actually possible in 2025 when you combine modern serverless infrastructure, AI-native development tools, and intelligent product design. The future of software development isn't just AI-assisted—it's AI-native, from the code we write to the features we ship.
Welcome to the era where solo developers build what used to require entire teams. Welcome to FinHub.
The Vision: What Makes FinHub Different
Most budgeting apps suck. They require manual transaction entry, don't sync with all your banks, use clunky categorization rules, and feel like homework. I wanted something that just worked—automatically, intelligently, instantly.
The problem I was solving:
- Manual expense tracking is tedious and error-prone
- Traditional budgeting apps require constant data entry
- Bank apps don't provide multi-account aggregation
- Generic categorization doesn't match my spending patterns
- International banking isn't well-supported
The AI-native solution I built:
Automatic Transaction Extraction: Your bank sends an email → FinHub parses it → transaction appears in the app. No manual entry, ever.
Intelligent Categorization: Claude AI learns from your spending history and categorizes transactions with context awareness.
Real-Time Processing: Gmail Pub/Sub webhooks trigger Cloud Functions the instant your bank email arrives. You get a notification on your iPhone before you finish swiping your card.
Multi-Currency Support: Colombian Pesos (COP) and USD with live exchange rates.
Learning System: Every time you correct a category, the AI gets smarter. Your app improves the more you use it.
Dark Cyberpunk UI: Minimalist, fast, focused.
The tech philosophy: AI everywhere. From development (Claude Code writing the features) to production (Claude AI categorizing your spending). Serverless-first so it scales automatically. Mobile-first so it fits in your pocket. Production-ready from day one with proper error handling, comprehensive tests, and security rules.
The Tech Stack: Architecture Deep Dive
Frontend: Flutter + Dart
Framework: Flutter 3.32.7
Language: Dart 3.8.1
Platform: iOS (iPhone)
UI: Material Design 3 with custom dark theme
State Management: Provider + Firestore streams
Navigation: go_router with auth guards
Why Flutter? Single codebase for beautiful native iOS UI. Hot reload means instant feedback during development. Excellent Firebase integration out of the box. Strong type safety with Dart catches bugs at compile time. Native performance without the complexity of Swift/SwiftUI.
Backend: Firebase Serverless Architecture
Authentication: Google Sign-In (OAuth 2.0, Gmail API scopes)
Database: Cloud Firestore (real-time NoSQL)
Compute: Cloud Functions (Node.js 20, TypeScript)
Messaging: Firebase Cloud Messaging + APNS
Firestore Collections:
users- Profiles, FCM tokens, Gmail OAuth tokenstransactions- Financial transactions with AI metadatawallets- Bank accounts (Bancolombia, Panama, Payoneer, Cash)categories- Spending categories (🏠 Rent, 🥩 Groceries, 🚕 Transport)keywordMappings- AI learning data for category matchingemailLogs- Audit trail of processed bank emailsnotifications- In-app notification history
Security Rules:
- User-scoped data isolation (you can only see your transactions)
- Protected system categories (💸 Other, 🏦 Banking, ⚖️ Adjustments)
- Write protection for critical fields (no tampering with balances)
Composite Indexes:
transactions(userId, timestamp DESC)- Main transaction listtransactions(userId, category, timestamp DESC)- Category filteringtransactions(userId, walletId, timestamp DESC)- Wallet filtering
Cloud Functions Architecture (TypeScript)
functions/
├── src/
│ ├── index.ts # Function entry points
│ ├── ai/
│ │ └── claude-service.ts # Claude AI integration
│ ├── gmail/
│ │ ├── auth-service.ts # OAuth token management
│ │ ├── gmail-service.ts # Gmail API client
│ │ └── watch-service.ts # Pub/Sub watch setup
│ ├── transactions/
│ │ └── transaction-builder.ts # Email → Transaction parser
│ └── notifications/
│ ├── push-notification-service.ts # FCM
│ └── in-app-notification-service.ts # Firestore
Key Cloud Functions:
processGmailNotification (Pub/Sub trigger)
- Triggered by Gmail Pub/Sub watch webhook
- Fetches new emails from Gmail API
- Identifies bank source (Bancolombia/Panama/Payoneer)
- Parses transaction details (amount, merchant, date)
- Calls Claude AI for categorization
- Creates transaction in Firestore
- Sends push notification to iPhone
triggerTokenRefresh (Scheduled, hourly)
- Ensures Gmail API access tokens stay fresh
- Rotates OAuth tokens automatically
- Re-establishes Pub/Sub watch if expired
updateExchangeRate (Scheduled, daily)
- Fetches COP/USD exchange rate
- Updates Firestore for multi-currency calculations
- Sends notification if rate changes significantly
storeAccessToken (Callable)
- Called from Flutter app after Google Sign-In
- Stores OAuth tokens in Firestore
- Sets up Gmail Pub/Sub watch
AI Integration: Claude Console API
interface ClaudeResponse {
type: "TRANSACTIONAL" | "INFORMATIONAL" | "UNKNOWN";
category?: string; // Emoji (🏠, 🥩, 🚕)
categoryDescription?: string; // "Rent", "Groceries", "Transport"
merchant?: string;
amount?: number;
confidence?: number; // 0-100
message?: string;
}
The AI Learning Loop:
- Email arrives → Claude analyzes content
- AI categorizes based on:
- Last 20 transactions (context)
- Last 50 user corrections (learning data)
- All available categories
- Email sender (Bancolombia/Panama/Payoneer)
- User reviews categorization
- If user changes category → stored in
keywordMappings - Future similar transactions → more accurate
Why Claude AI:
- Superior language understanding for transaction descriptions
- Context-aware categorization learns from history
- Handles Spanish/English mixed content (Colombian banks)
- API simplicity (single endpoint, clean responses)
- Cost-effective for low-volume personal use (~$5/month)
The Real Innovation: Gmail Pub/Sub Architecture
Traditional polling sucks. Checking Gmail every 5 minutes means delayed notifications, wasted API calls, and poor user experience. Users want instant notifications when money is spent—not 5 minutes later.
The Pub/Sub solution:
graph LR
A[Bank Email Sent] --> B[Gmail Receives]
B --> C[Gmail Pub/Sub Watch]
C --> D[Cloud Pub/Sub Topic]
D --> E[processGmailNotification]
E --> F[Gmail API Fetch]
F --> G[Claude AI Categorization]
G --> H[Firestore Write]
H --> I[FCM Push Notification]
I --> J[iPhone Lock Screen]
style A fill:#ff6b6b
style C fill:#45b7d1
style E fill:#ffeaa7
style G fill:#fd79a8
style I fill:#a29bfe
style J fill:#6c5ce7
How Gmail Pub/Sub Watch Works:
1. Setup Phase (after user signs in):
await gmail.users.watch({
userId: 'me',
requestBody: {
topicName: 'projects/finhub-563a5/topics/gmail-notifications',
labelIds: ['INBOX']
}
});
2. Email Arrives:
- Bank sends transaction email to user@gmail.com
- Gmail immediately publishes notification to Pub/Sub topic
- No delay, no polling required
3. Cloud Function Triggered:
export const processGmailNotification = onMessagePublished(
{ topic: "gmail-notifications" },
async (event) => {
const historyId = event.data.message.json.historyId;
// Fetch new emails since last history ID
// Process each email...
}
);
4. Email Processing Pipeline:
- Identify sender (Bancolombia/Panama/Payoneer filters)
- Extract transaction data (regex parsing + Claude AI)
- Determine wallet (based on email source)
- Create transaction in Firestore
- Update wallet balance
- Send push notification
5. Push Notification to iPhone:
await admin.messaging().send({
token: userFCMToken,
notification: {
title: "💰 New Transaction",
body: "🥩 Groceries - $45,000 at Éxito"
},
apns: {
payload: {
aps: {
sound: "default",
badge: 1,
"content-available": 1 // Critical for background delivery
}
}
}
});
Token Management Challenge:
- Gmail Pub/Sub watch expires after 7 days
- OAuth access tokens expire after ~1 hour
- Solution: Scheduled Cloud Function runs hourly to refresh
Performance Metrics:
- Email arrival → Notification on phone: < 5 seconds
- Zero polling overhead
- Scales automatically with Firebase
- Works even when app is closed
The AI Development Process: Building with Claude Code
This is where things get meta. I used AI to build an AI-powered app.
The AI Trifecta:
- Claude Code - Primary development AI (autonomous coding)
- Cursor - AI-powered code editor (context-aware completions)
- Cursor Bug Bot - Automated PR review (catches bugs before merge)
How Claude Code Accelerated Development
Day 1-2: Project Setup & Architecture
Me: "Start mvp"
Claude Code:
- Loads PRD from .claude/prd/mvp/
- Presents 8-phase development plan
- Asks for confirmation
Me: "Proceed"
Claude Code:
- Creates Flutter project
- Configures Firebase
- Sets up folder structure
- Deploys security rules
- Commits + pushes to GitHub
No boilerplate hell. No "let me Google how to set up Firebase." Just describe what you want, confirm the plan, and Claude Code executes.
Day 3-5: Data Models & Backend Services
- Claude Code wrote 7 Firestore data models with JSON serialization
- Generated CRUD services for each collection
- Created default data initialization logic
- All with proper error handling, logging, and type safety
- Human input: Reviewed architecture decisions, adjusted business logic
Day 6-8: Gmail API Integration This was the most complex part: OAuth flows, token management, Pub/Sub webhooks.
Claude Code:
- Researched Gmail API documentation
- Implemented OAuth 2.0 flow with refresh token handling
- Set up Pub/Sub watch with automatic renewal
- Created email parsing logic for Bancolombia/Panama/Payoneer
- Built transaction extraction pipeline
Human oversight: Tested with real bank emails, refined regex patterns, handled edge cases.
Day 9-10: Claude AI Integration Claude Code integrated its own API (yes, the AI coded itself into the product).
- Designed learning loop architecture
- Implemented category suggestion logic
- Built feedback collection system
- Human decision: Chose Claude over GPT for superior context handling
Day 11-12: UI/UX Implementation
- Dark cyberpunk theme with custom color palette
- Transaction list with real-time Firestore streams
- Wallet management screens
- Category selection with emoji support
- Reports and analytics
- Human touch: Final design tweaks, animation timing
Day 13-14: Testing, Bug Fixes, Push Notifications
- Comprehensive test suite (65 tests)
- Fixed edge cases (floating-point precision, multi-currency)
- Implemented FCM + APNS for background notifications
- Deployed to production
The Claude Code Workflow Pattern
# 1. Start a session
start mvp
# 2. Claude Code:
# - Pulls latest from GitHub
# - Loads PRD context
# - Identifies next task
# - Presents plan
# - WAITS for confirmation
# 3. Developer confirms or adds requirements
proceed # or "add error handling" or "use different approach"
# 4. Claude Code:
# - Implements the feature
# - Writes tests
# - Commits changes
# - Updates documentation
# - Pushes to GitHub
# - ALL AUTOMATICALLY
# 5. Cursor Bug Bot reviews PR
# - Identifies issues
# - Suggests improvements
# - Claude Code fixes them
What Claude Code Did Well
✅ Boilerplate code generation (models, services, widgets) ✅ Firebase integration (auth, Firestore, functions) ✅ TypeScript backend services ✅ Testing infrastructure ✅ Documentation maintenance ✅ Git workflow automation
Where Human Judgment Was Critical
🧠 Product decisions (which features, UX flows) 🧠 Architecture choices (Firebase vs custom backend) 🧠 Security considerations (Firestore rules, token storage) 🧠 Business logic edge cases (multi-currency calculations) 🧠 Design aesthetics (color palette, spacing, animations)
Productivity Metrics
- Lines of code written: ~15,000 (Dart + TypeScript)
- Time to first working prototype: 3 days
- Time to production-ready app: 14 days
- Human coding time: ~30 hours (reviewing, testing, refining)
- AI coding time: ~50+ hours equivalent (boilerplate, services, tests)
- Speed multiplier: 3-4x faster than solo human development
The AI didn't replace me. It amplified me. I spent my time on what humans do best—product thinking, architecture decisions, UX refinement—while the AI handled what it does best—code generation, boilerplate, testing, documentation.
Technical Challenges & Solutions
Challenge 1: iOS Push Notifications Background Delivery
Problem: Notifications showed when app was open, but NOT when closed/background.
Root Cause: Missing content-available: 1 flag in APNS payload.
Solution:
apns: {
payload: {
aps: {
"content-available": 1, // Wakes iOS to deliver notification
sound: "default",
badge: 1
}
}
}
Time to debug: 4 hours across multiple deploy cycles. This was the kind of thing Claude Code couldn't intuit—it required iOS-specific knowledge and testing on physical hardware.
Challenge 2: Multi-Currency Balance Calculations
Problem: Floating-point precision errors in financial calculations. You can't have $1,000.000000000001 in your account.
Solution:
// DON'T: wallet.balance += amount (floating-point errors)
// DO: Update with set operation
wallet.balanceCOP = (wallet.balanceCOP + amountCOP).roundToDouble();
wallet.balanceUSD = (wallet.balanceUSD + amountUSD).roundToDouble();
Testing: 65 comprehensive tests covering edge cases like currency conversions, transaction reversals, and balance adjustments.
Challenge 3: Gmail OAuth Token Management
Problem: Tokens expire, Pub/Sub watch expires after 7 days, users lose email processing capability.
Solution:
- Scheduled hourly token refresh
- Automatic Pub/Sub watch renewal
- Graceful fallback if refresh fails
- User notification when re-auth needed
Challenge 4: Email Parsing Variability
Problem: Banks change email formats, different languages (Spanish/English), inconsistent merchant names.
Solution:
- Claude AI instead of rigid regex
- AI learns from corrections (keyword mappings)
- Confidence scores for uncertain categorizations
- Manual review for low-confidence transactions
This is where AI shines—handling ambiguity and learning from feedback.
System Architecture
Here's how all the pieces fit together:
graph TB
subgraph "User Device - iOS"
A[FinHub Flutter App]
B[FCM SDK]
C[Firestore SDK]
end
subgraph "Firebase Cloud"
D[Firebase Auth]
E[Cloud Firestore]
F[Cloud Functions]
G[Cloud Messaging]
end
subgraph "External APIs"
H[Gmail API]
I[Gmail Pub/Sub]
J[Claude AI API]
end
A --> D
A --> C
C --> E
B --> G
H --> I
I --> F
F --> E
F --> J
F --> G
G --> B
style A fill:#6c5ce7
style E fill:#00b894
style F fill:#fdcb6e
style J fill:#fd79a8
Email Processing Flow
sequenceDiagram
participant Bank
participant Gmail
participant PubSub as Gmail Pub/Sub
participant CF as Cloud Function
participant Claude as Claude AI
participant FS as Firestore
participant FCM
participant Phone as iPhone
Bank->>Gmail: Send transaction email
Gmail->>PubSub: Publish notification
PubSub->>CF: Trigger processGmailNotification
CF->>Gmail: Fetch email via API
Gmail-->>CF: Email content
CF->>CF: Parse transaction data
CF->>Claude: Categorize transaction
Claude-->>CF: Category + confidence
CF->>FS: Create transaction
CF->>FS: Update wallet balance
CF->>FCM: Send push notification
FCM->>Phone: Deliver to lock screen
The Result: Production-Ready in 2 Weeks
What Got Built:
✅ Fully functional iOS app ✅ Real-time email processing via Gmail Pub/Sub ✅ AI-powered transaction categorization ✅ Multi-currency support (COP/USD) ✅ Push notifications (foreground + background) ✅ 65 passing tests ✅ Secure Firestore rules ✅ Automatic token refresh ✅ Dark cyberpunk UI ✅ Transaction management (add/edit/delete) ✅ Wallet management ✅ Reports & analytics ✅ Category management
Production Metrics:
- Deployment: Live on Firebase Production
- Processing Speed: Email → Notification in < 5 seconds
- Uptime: 99.9% (Firebase SLA)
- Cost: ~$5/month (Firebase free tier + Claude API)
- Scalability: Handles 100+ transactions/month effortlessly
User Experience:
- User makes purchase at grocery store
- Bank sends email within 30 seconds
- Gmail receives email
- Pub/Sub triggers Cloud Function
- Claude AI categorizes as "🥩 Groceries"
- Transaction saved to Firestore
- Push notification appears on iPhone lock screen
- User taps notification → app opens to review
- User confirms or corrects category
- Wallet balance updates automatically
Total Development Time: 14 days, ~80 hours
Lessons Learned & The Future of AI Development
Key Insights
1. AI Amplifies, Not Replaces
Claude Code handled 70% of implementation. Human judgment was crucial for 30% (architecture, UX, security). Best results come from treating AI as a coding partner, not a replacement developer.
The magic happens in the collaboration. AI generates, human validates. AI implements, human architects. AI tests, human designs.
2. Serverless = Speed
No infrastructure management. No DevOps nightmares. Firebase handles scaling, backups, security. I focused on features, not servers.
Trade-off: Vendor lock-in and cold starts. But for a solo developer? Worth it.
3. AI-Native Product Design
Building AI into the product from day one = game changer. Claude AI categorization >> rule-based systems. Learning loops create moats—your app gets smarter over time. Confidence scores enable graceful UX (auto-confirm high confidence, review low confidence).
This isn't "we added AI." This is "AI is fundamental to how the product works."
4. Testing is Non-Negotiable
Financial apps demand precision. 65 tests caught critical bugs before production. Test-driven development + AI = faster iteration without breaking things.
5. Modern Stack = Modern Speed
- Flutter: Beautiful iOS UI with minimal code
- Firebase: Backend without backend
- TypeScript: Type safety in Cloud Functions
- Claude AI: Intelligent features without ML expertise
The stack matters. Choose tools designed for rapid iteration.
What This Means for Development
2 weeks for a production app was unthinkable 2 years ago. AI coding assistants are now essential tools. The bottleneck is shifting from "can we build it" to "should we build it."
Solo developers can now ship enterprise-quality products. The future: 1-person unicorns.
The AI-Native Era
We're entering a paradigm shift where:
- Apps ARE intelligent (Claude AI categorization, not just UI)
- Development IS AI-assisted (Claude Code, Cursor, Bug Bot)
- Production IS fast (2 weeks from zero to live)
- Quality IS high (comprehensive testing, error handling, security)
FinHub represents this future:
- Built WITH AI (Claude Code for development)
- Powered BY AI (Claude API for categorization)
- Deployed ON serverless (Firebase scales automatically)
- Delivered IN two weeks (from idea to production)
For Developers:
The tools exist TODAY to build AI-native products at unprecedented speed. The question isn't "how" anymore—it's "what are you building?"
Stop waiting for permission. Stop waiting for funding. Stop waiting for the perfect team. You have everything you need right now: Claude Code, Firebase, Flutter. The barriers are gone.
For Founders:
MVP timelines are shrinking from months to weeks. Technical founders can ship production-ready products solo. Non-technical founders can prototype faster than ever with AI coding assistants.
The competitive advantage is no longer "who can build it" but "who builds it first."
The Cyberpunk Future is Now:
We're living in a world where:
- Your banking app learns your spending patterns
- Emails become structured data automatically
- Notifications arrive before you finish swiping your card
- One developer builds what used to require a team
- AI writes itself into the product
Technical Deep Dive: Architecture & Code
Data Model
erDiagram
USER ||--o{ TRANSACTION : creates
USER ||--o{ WALLET : owns
USER ||--o{ CATEGORY : defines
USER ||--o{ KEYWORD_MAPPING : learns
TRANSACTION {
string id PK
string userId FK
float amount
string category
string walletId FK
timestamp timestamp
string note
string type
string aiConfidence
}
WALLET {
string id PK
string userId FK
string name
float balanceCOP
float balanceUSD
bool isDefault
}
CATEGORY {
string id PK
string userId FK
string emoji
string description
bool isProtected
}
AI Learning Loop
graph TD
A[Email Arrives] --> B[Extract Transaction Data]
B --> C[Load Context:<br/>Last 20 Transactions]
C --> D[Load Learning Data:<br/>Last 50 Corrections]
D --> E[Load Categories]
E --> F[Send to Claude AI]
F --> G{Confidence > 80%?}
G -->|Yes| H[Auto-Categorize]
G -->|No| I[Request User Review]
H --> J[Create Transaction]
I --> K[User Confirms/Corrects]
K --> L{Category Changed?}
L -->|Yes| M[Store in keywordMappings]
L -->|No| J
M --> J
J --> N[Future Transactions<br/>Learn from Correction]
style F fill:#fd79a8
style M fill:#ffeaa7
style N fill:#00b894
Key Code: Cloud Function Email Processing
export const processGmailNotification = onMessagePublished(
{ topic: "gmail-notifications", region: "us-central1" },
async (event) => {
const data = event.data.message.json;
const emailAddress = data.emailAddress;
const historyId = data.historyId;
// Get user by email
const usersSnapshot = await db
.collection("users")
.where("email", "==", emailAddress)
.limit(1)
.get();
if (usersSnapshot.empty) {
logger.warn("No user found for email", { emailAddress });
return;
}
const userId = usersSnapshot.docs[0].id;
const userData = usersSnapshot.docs[0].data();
// Get Gmail API credentials
const oauth2Client = new google.auth.OAuth2();
oauth2Client.setCredentials({
access_token: userData.gmailAccessToken,
refresh_token: userData.gmailRefreshToken,
});
const gmail = google.gmail({ version: "v1", auth: oauth2Client });
// Fetch history since last ID
const history = await gmail.users.history.list({
userId: "me",
startHistoryId: userData.lastHistoryId || historyId,
historyTypes: ["messageAdded"],
});
for (const record of history.data.history || []) {
for (const message of record.messagesAdded || []) {
await processNewEmail(userId, message.message.id, gmail);
}
}
// Update last history ID
await db.collection("users").doc(userId).update({
lastHistoryId: historyId,
});
}
);
Key Code: Flutter Real-time Transaction Stream
class TransactionService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
/// Stream of transactions for current user
Stream<List<Transaction>> streamTransactions({
String? categoryFilter,
String? walletFilter,
}) {
final user = _auth.currentUser;
if (user == null) return Stream.value([]);
Query query = _firestore
.collection('transactions')
.where('userId', isEqualTo: user.uid)
.orderBy('timestamp', descending: true)
.limit(100);
// Apply filters
if (categoryFilter != null) {
query = query.where('category', isEqualTo: categoryFilter);
}
if (walletFilter != null) {
query = query.where('walletId', isEqualTo: walletFilter);
}
return query.snapshots().map((snapshot) {
return snapshot.docs.map((doc) {
final data = doc.data() as Map<String, dynamic>;
data['id'] = doc.id;
return Transaction.fromJson(data);
}).toList();
});
}
}
Tech Stack Summary
| Layer | Technology | Purpose | Why Chosen |
|---|---|---|---|
| Frontend | Flutter 3.32.7 | iOS app | Single codebase, hot reload, native performance |
| Dart 3.8.1 | Language | Type-safe, async/await, great tooling | |
| Backend | Firebase | Serverless | No infrastructure, auto-scaling, built-in auth |
| Cloud Firestore | NoSQL DB | Real-time streams, offline support | |
| Cloud Functions | Compute | TypeScript, event-driven, auto-scale | |
| AI | Claude Console | Categorization | Superior understanding, context-aware |
| Gmail API | Email fetch | OAuth 2.0, reliable, well-documented | |
| Gmail Pub/Sub | Webhooks | Instant notifications, no polling | |
| Dev Tools | Claude Code | AI coding | Boilerplate generation, Git automation |
| Cursor | AI editor | Context completions, refactoring | |
| Language | TypeScript | Functions | Type safety, better tooling |
Conclusion
The cyberpunk future isn't coming—it's here. We're living in an era where AI writes code, emails become data instantly, and solo developers ship products that feel like they came from entire engineering teams.
FinHub took 14 days because I had the right tools: Claude Code for development, Claude AI for intelligence, Firebase for infrastructure, Flutter for beautiful UI. The technical barriers have collapsed. The only limit is your imagination and willingness to ship.
The AI-native era rewards builders. Not planners. Not waiters. Builders.