How I Bypassed App Stores with a Workout PWA That Works Everywhere
- PWA
- Progressive Web App
- mobile development
- fitness tech
- workout
- Lemon Squeezy
The Truth About Building Apps Without App Stores
You don’t need Apple or Google’s permission to ship a professional mobile app. With my friend who is a sports coach, I built WorkoutGen - a workout generator that creates personalized gym weight training programs - as a Progressive Web App that works seamlessly across all devices without touching the App Store or Google Play.
This article shares my real-world experience building and deploying a production PWA, including the economic advantages (keeping 34% more revenue per customer), the technical challenges (especially Safari’s video playback bugs), and the user adoption strategies I implemented. The reality is more nuanced than the marketing hype, but the opportunities are real.
What Makes a Great Fitness PWA?
A professional workout PWA must deliver (and WorkoutGen implements all of these):
- Offline-first architecture - Workouts accessible without internet
- Native-like performance - Smooth animations, instant responses
- Media handling - Exercise videos or GIFs that work on all platforms
- Install prompts - Clear onboarding for users unfamiliar with PWAs
- Payment integration - Direct Lemon Squeezy billing (0% platform fees vs 30% store cuts)
- Cross-platform consistency - Identical experience on iOS, Android, desktop
Why I Chose PWA Over Native Apps
The Economics Are Compelling
Traditional App Stores:
- 30% commission on all transactions (Apple, Google)
- Annual developer fees ($99-$299)
- Mandatory in-app purchase system
- Revenue share on subscriptions (year 1: 30%, year 2+: 15%)
PWA + Lemon Squeezy (Merchant of Record):
- 0% platform commission
- 5% + 50¢ Lemon Squeezy fee (includes payment processing + tax compliance)
- Lemon Squeezy handles all VAT/GST/sales tax globally - no need to register in each country
- Full control over billing logic
- Instant payment updates (no 24-48h App Store delays)
For a $10/month subscription, you keep $7.00 with app stores vs $9.00 with Lemon Squeezy. That’s ~29% more revenue per customer. Lemon Squeezy handles 100% of tax compliance and legal obligations in every country - you don’t need to declare anything.
The Development Reality
| Feature | Native Apps | PWA |
|---|---|---|
| Codebase | 2-3 separate (iOS/Android/Web) | Single codebase |
| Updates | Store review (3-7 days) | Instant deployment |
| Distribution | Store approval required | Direct URL sharing |
| Installation | 50MB+ downloads | 2-5MB cached assets |
| Offline support | Manual implementation | Service Worker standard |
What About Cross-Platform Solutions?
Before going all-in on PWA, I evaluated the major cross-platform alternatives:
React Native / Flutter: Promised “write once, run anywhere” but still required:
- Separate iOS and Android build pipelines
- Platform-specific code for native features
- App store submissions and approval delays
- 40-60MB+ app downloads
- Learning Dart (for Flutter) - a new language with smaller ecosystem than JavaScript
Capacitor (Hybrid Wrapper): Useful for accessing native APIs, but adds complexity:
- 15MB overhead just for the wrapper
- Still need app store approval for distribution
- Loses instant update advantage of pure web apps
- Best used as optional enhancement, not requirement
PWA + Capacitor (My Choice): I built WorkoutGen as a pure PWA first, with optional Capacitor wrapper for native features later. This gives:
- Core experience works everywhere via web (no store required)
- Native app wrappers available for App Store discovery
- Single codebase with conditional native enhancements
- Deploy web updates instantly, native updates when needed
The Technical Challenges (And Solutions)
iOS Video Playback: Years-Old Safari Bugs
The Problem That Won’t Die:
Safari on iOS has persistent, unfixed video playback bugs that have plagued PWA developers for years. This isn’t speculation - it’s documented in WebKit’s own bug tracker:
Recent Issues:
- iOS 26 (2025): Videos freeze on first frame after closing and reopening PWA (WebKit Bug #300990)
- iOS 15-18 (ongoing): getUserMedia() video streams occasionally display black screen in PWAs (WebKit Bug #252465, reported Feb 2023, marked “fixed” but still occurring)
Historical Patterns:
- Bug #232076 (2021): Videos from blob URLs consumed 200-300MB memory for 2MB videos, took 10+ seconds to start playing. Fixed in iOS 15 after months of investigation.
- Bug #198277 (2019-2022): Audio stopped playing when PWAs were backgrounded. Took 3 years to fix (finally resolved in iOS 15.4, Feb 2022).
- Stack Overflow reports: Videos served from
/public/directory fail to load after page refresh or PWA installation on Safari (both iOS and macOS). Workaround: host videos on external CDN.
The pattern is clear: video/media bugs in Safari PWAs get reported, sometimes marked “fixed,” then resurface in new iOS versions.
My Workaround Strategy:
For WorkoutGen, I implemented multiple fallback approaches:
- Use
crossorigin="anonymous"attribute on all video elements - Add
playsInline,muted, andautoPlayfor iOS compatibility - Preload video metadata to reduce loading issues
- Monitor for iOS version-specific bugs and adapt (separate videos for iOS 26)
- Design UX assuming videos might fail - provide text instructions as backup
The frustrating reality: Bugs like #198277 took 3 years to fix, and supposedly “resolved” issues like getUserMedia() video playback (#252465) continue appearing in new iOS versions. Apple’s PWA support clearly receives less priority than native app APIs, with fixes requiring “changes at the underlying platform level” rather than quick WebKit patches.
No Native Install Events on iOS
Problem: Safari doesn’t fire beforeinstallprompt events, making it impossible to:
- Show native install prompts programmatically
- Detect if users already installed your PWA before page load
- Track installation success without manual user action
My Solution: Video Tutorial Overlays
Since iOS users can’t get automatic install prompts, I built a custom onboarding flow:
// Detection logic from WorkoutGen
const isPWAInstalled = () => {
// iOS detection
if (window.navigator.standalone) return true
// Android/Desktop
if (window.matchMedia("(display-mode: standalone)").matches) return true
return false
}
// Show tutorial for browsers without beforeinstallprompt
const browserInfo = detectBrowser()
if (!browserInfo.canInstallPWA && !isPWAInstalled()) {
showVideoTutorial() // Custom tutorial with installation instructions
}
WorkoutGen displays short video tutorials showing users exactly how to tap “Share → Add to Home Screen” on iOS. The videos are contextual - different instructions for iOS 26 vs earlier versions, Safari vs Chrome on iOS.
This UX investment was critical: without native install prompts, PWA adoption on iOS depends entirely on user education.
Service Worker Caching Strategy
Offline workouts require aggressive caching. WorkoutGen uses Vite PWA Plugin with Workbox:
// Real config from apps/frontend/vite.config.ts
VitePWA({
registerType: "prompt",
workbox: {
globPatterns: ["**/*.{js,css,html,ico,png,svg,woff,woff2}"],
globIgnores: ["**/*.mp4", "**/*.webm", "**/*.gif", "**/*.jpg", "**/*.jpeg", "**/*.webp"],
maximumFileSizeToCacheInBytes: 40 * 1024 * 1024,
cleanupOutdatedCaches: true,
navigateFallback: null,
},
})
Key decisions:
- Cache static assets (JS, CSS, fonts) for instant loading
- Exclude large media from precache - fetch on demand via CDN
- 40MB cache limit to avoid storage quota issues on iOS (Safari caps at 50MB)
registerType: 'prompt'lets users control when to update- Runtime caching for exercise media with CacheFirst strategy
This gives WorkoutGen full offline functionality while staying within iOS storage limits.
What Disappointed Me About PWAs (And Why I’m Still Bullish)
Limited Browser Support for Install Features
Safari (iOS): No beforeinstallprompt event, no automatic install UI, no install banner. Users
must manually navigate through Share menu → “Add to Home Screen” - a 4-tap process most people don’t
know exists.
Firefox (Desktop): Inconsistent PWA installation support. Some versions support it, others don’t. The install button appears unpredictably.
Chrome/Edge (Android): These work perfectly with native install prompts, but they’re the minority globally when you factor in iOS market share.
The workaround? Build custom install UX for every browser. For WorkoutGen, I implemented:
- Video tutorials showing iOS installation steps
- Browser detection to show relevant instructions
- Custom “Add to Home Screen” buttons that trigger contextual guides
- Different flows for iOS 26 vs earlier versions
This adds development overhead, but it’s manageable compared to maintaining separate native codebases.
Most Users Don’t Know PWAs Exist
This is the bigger challenge. When you tell someone “install this app,” they expect to go to an app store. Explaining “actually, tap this button in your browser” creates friction.
I’ve found two strategies work:
- Don’t call it a PWA - just say “Add to your home screen for the full experience”
- Show, don’t tell - use video tutorials that demonstrate the installation process
User education is real work, but it’s worth it to avoid 30% platform fees and gain instant deployment.
Why I’m Still Bullish Despite These Limitations
Here’s the thing: PWAs are massively underrated for a specific class of applications.
For SaaS products, content platforms, and tools like WorkoutGen:
- ✅ You ship to all platforms from day one (iOS, Android, desktop, web)
- ✅ Full-screen, offline-capable experience without app stores
- ✅ Instant updates without review delays
- ✅ Keep ~90% of revenue instead of 70% (Lemon Squeezy 5%+50¢ vs app store 30%)
- ✅ Direct relationship with users (no platform intermediary)
Yes, Safari support is frustrating. Yes, user awareness is low. But these are solvable UX problems, not fundamental technical limitations. The core technology works beautifully - it’s just underutilized because most developers default to native apps without evaluating PWAs seriously.
What About AI/ChatGPT for Workout Planning?
ChatGPT can generate workout plans, but it falls short for serious training:
- No exercise video library - ChatGPT can describe exercises, but can’t show you proper form. WorkoutGen includes a custom-built video database covering every major strength training exercise with professional demonstrations.
- Structured progression - No tracking of weekly progressive overload, periodization, or deload weeks
- Offline access - Requires internet for every query (unusable in gyms with poor signal)
- Personalization memory - Loses context between sessions unless you manually maintain conversation history
- No execution interface - You get a text plan, but no workout player to guide you through sets, rest periods, and exercise order
Building WorkoutGen’s exercise video database was months of work - filming, editing, compressing, and optimizing for mobile playback. This is something AI can’t provide because it operates purely in text. The combination of structured algorithms + visual demonstrations + offline access is where PWAs shine over conversational AI.
What About YouTube for Workout Tutorials?
YouTube is incredible for learning exercises but terrible for actual training:
- No workout structure - You piece together 6 different videos
- Ads interrupt sets - Kills momentum mid-workout
- No progression tracking - Can’t log reps, weight, or improvements
- Internet required - Unusable in gyms with poor signal
A PWA gives you the best of both: curated video content inside a structured training system.
My Honest Recommendation
For developers building fitness/productivity/SaaS apps:
- Start with PWA unless you absolutely need native APIs (HealthKit, ARKit, Background App Refresh)
- Use React 19 + Vite 7 + Vite PWA Plugin + Workbox for optimal developer experience
- Add Capacitor later only if you want optional native wrappers for app store presence
- Test on real iOS devices throughout development - Safari’s PWA bugs will surprise you
- Budget time for custom install UX (video tutorials, browser detection, contextual prompts)
For entrepreneurs and bootstrapped founders:
- PWA-first for MVPs - Ship to all platforms in weeks, not months, with a single codebase
- Economics favor PWAs - Keep ~90% of revenue (Lemon Squeezy 5%+50¢) vs 70% (app stores 30%)
- Instant iteration - Deploy updates in seconds without store review delays
- App stores optional - Add native wrappers later for discovery if needed, but core product works everywhere via web
- Consider native apps only after $10K+ MRR to justify the overhead
For users:
- Try WorkoutGen PWA: go.workoutgen.app/start
- Install via browser menu: tap Share → “Add to Home Screen” (iOS) or browser install button (Android)
- Works offline, full-screen, feels like a native app - without the 50MB+ download
The Bottom Line
After building WorkoutGen as a production PWA, here’s what I learned:
The Good:
- ✅ Keep ~25% more revenue per customer - Lemon Squeezy 5%+50¢ vs App Store 30% (plus Lemon Squeezy handles all tax compliance)
- ✅ Ship to all platforms from one codebase - iOS, Android, desktop, web simultaneously
- ✅ Deploy updates instantly - No store review delays (hours vs days)
- ✅ Full offline functionality - Service Workers cache everything users need
- ✅ Faster development velocity - No native build toolchains or platform-specific APIs
The Challenges:
- ⚠️ Safari PWA support is frustrating - Video playback bugs unfixed for years, limited APIs
- ⚠️ No automatic install prompts on iOS - Requires custom UX and user education
- ⚠️ Browser compatibility varies - What works in Chrome may break in Safari
- ⚠️ User awareness is low - Most people don’t know PWAs exist
- ⚠️ No app store discovery - You need organic traffic or paid acquisition
The Verdict:
PWAs are massively underrated for specific use cases: SaaS tools, productivity apps, content platforms, and applications like WorkoutGen where structured offline access matters more than app store placement.
The technology is mature and production-ready. The challenges are primarily UX and user education - solvable problems, not fundamental limitations. If you’re building an app where you can drive your own traffic and want to avoid platform fees, PWAs offer a compelling alternative to native development.
WorkoutGen proves it works at scale. The 2-3 problems (Safari bugs, user education) are far outweighed by the benefits: instant deployment, zero platform fees, and true cross-platform reach.
Try the PWA That Proves It Works
WorkoutGen generates personalized workout plans optimized for muscle growth. Install it in 10 seconds, no app store required.
Start Your Free WorkoutFAQ
Q: Can PWAs really replace native apps for fitness applications?
Yes, for most fitness apps. WorkoutGen proves you can build a full-featured workout platform with offline support, video demonstrations, and subscription billing entirely as a PWA. The exceptions are apps requiring HealthKit integration, Apple Watch complications, or background GPS tracking - those need native code. But for strength training, HIIT, yoga, or similar structured workouts, PWAs deliver everything users need.
Q: How do you handle the lack of automatic install prompts on iOS?
Video tutorials and contextual education. On WorkoutGen, when we detect an iOS user without the
beforeinstallprompt event, we show a short video demonstrating exactly how to tap Share → “Add to
Home Screen.” We trigger this after users have experienced value (e.g., completed their first
workout) so they’re motivated to install. It adds development work, but it’s far less than
maintaining a native iOS app.
Q: What about app store discovery - don’t you lose all that organic traffic?
Yes, this is PWA’s biggest weakness. You can’t be discovered by browsing the App Store. You need your own traffic sources: SEO, content marketing, social media, or paid ads. For WorkoutGen, I focus on organic search for terms like “workout generator” and “personalized training program.” If app store discovery is critical to your business model, consider a hybrid approach: PWA as the core product, with optional native wrappers submitted to stores for visibility.
Q: Do PWAs actually work offline on iOS, or is that just marketing?
They absolutely work offline - this is one area where iOS PWA support is solid. Service Workers have been fully supported since iOS 11.3 (2018). WorkoutGen caches workout data, exercise videos, and app assets so users can train in airplane mode or gyms with no signal. The catch is the 50MB cache limit on Safari, but for most apps that’s plenty.
Q: What’s the file size compared to native apps?
WorkoutGen’s PWA is 2.8MB of cached assets (JavaScript, CSS, fonts, UI images). Exercise media is fetched on-demand from CDN and cached. An equivalent native app would be 40-60MB minimum, likely more with embedded video files. PWAs also update incrementally - only changed files download, not the entire app.
Q: Can you actually make money with a PWA, or do you need in-app purchases?
You make more money with PWAs using Lemon Squeezy as Merchant of Record. No 30% app store cut, no restrictions on pricing, instant payment processing. WorkoutGen users subscribe via Lemon Squeezy Checkout - I keep ~90% of revenue after Lemon Squeezy’s 5%+50¢ fee. With app stores, I’d keep only 70% (or 85% after year 1). For a $10/month subscription, that’s $9.00 vs $7.00 per customer.
The key advantage: Lemon Squeezy handles all tax compliance globally. They collect and remit VAT, GST, and sales tax in every country. I don’t need to register for tax in 40+ countries or handle legal compliance - Lemon Squeezy is the legal seller. Simple pricing, zero tax paperwork.
Q: What tech stack did you use to build WorkoutGen?
- Frontend: React 19 with TypeScript (strict mode)
- Build tool: Vite 7 with Vite PWA Plugin
- PWA tooling: Workbox for service worker caching strategies
- State management: TanStack Query for server state, TanStack Store for UI state
- UI framework: shadcn/ui + Tailwind CSS v4 for responsive mobile-first design
- Backend: Strapi v5 CMS with custom workout generation API
- Payments: Lemon Squeezy for subscriptions
- Hosting: Frontend on Cloudflare Pages, backend on dedicated servers
- Optional native: Capacitor 7 for future native wrappers (not required for core PWA)