TaleCards
Personalized daily bedtime stories that grow with your child
TaleCards computes each child's age in days and serves an age-appropriate story every day, with a 3-day trial funneling into a RevenueCat subscription. Shipped through 14 release builds with four auth providers, Crashlytics, App Check and timezone-aware daily story notifications.
The Problem
Parents want stories matched to their child's exact developmental stage, and the content team needed to expand age coverage without shipping app updates.
The Solution
Stories live in Firestore buckets keyed by day-age; the active content window is remotely configurable, and a modulo-wrap fallback guarantees a story every single day regardless of catalog gaps. RevenueCat entitlement changes sync to Firestore in real time, flipping the paywall reactively.
Architecture
- Flutter + GetX with feature-first modules and documented Firestore schema
- Multi-kid profiles → age-in-days → story-bucket mapping with live-streamed remote config
- Auth: email, Google, Apple (crypto nonce + secure-storage of Apple's one-shot profile data), phone OTP
- RevenueCat customer-info listener → Firestore isPremium → reactive paywall stream
- Timezone-aware daily local notifications with downloaded cover images
Challenges & How I Solved Them
Never leave a child without a story
If a day-age bucket is empty, a modulo-wrap fallback re-maps into covered ranges: content gaps degrade gracefully instead of breaking the daily ritual.
Apple Sign-In done right
Apple provides name/email exactly once; they're cached in secure storage with a nonce-verified flow so account recovery works forever after.
My Contributions
- Entire product: app, Firestore schema, subscriptions, notifications, release pipeline
Impact
- Live on the Play Store and App Store: 14 release builds shipped (v1.0.7)
- Full trial→subscription funnel in production
Lessons Learned
Subscription apps are state machines: trial, entitlement, and content-window states must each have a single source of truth or the paywall lies to someone.