Convert Your Web App to iOS and Android Apps
Convert your web app to a mobile app for iOS and Android with native billing, push, and offline support, from the codebase you already have.

Your web app already works. React, Next, Vue, or something an AI builder generated, it does not matter: the browser version is done. The question is what happens when you try to put it in the App Store and Google Play, because the things most likely to block you have nothing to do with your frontend. They are billing, Apple's Guideline 4.2, and the second codebase most conversion routes quietly hand you. Solve those first.
The problem that actually gets web apps rejected
Apple requires In-App Purchases for digital content and Google requires Play Billing. If your app sells subscriptions or premium features through Stripe, that flow cannot ship in the store build, and review ends there. This is the single most common rejection for converted web apps, and it is a hard blocker, not a polish item.
Despia includes RevenueCat in every app, working on both iOS and Android today. Your web code decides at runtime whether it is running inside the native app, and routes payment accordingly:
import despia from 'despia-native'
const isDespia = navigator.userAgent.toLowerCase().includes('despia')
if (isDespia) {
// native paywall configured in the RevenueCat dashboard
despia(`revenuecat://launchPaywall?external_id=${userId}&offering=default`)
} else {
// web checkout, linked to the same account
window.location.href = `https://pay.rev.cat/<your_token>/${encodeURIComponent(userId)}`
}
Entitlements are checked on app load and work offline:
window.onRevenueCatPurchase = () => checkEntitlements()
if (isDespia) {
const data = await despia('getpurchasehistory://', ['restoredData'])
const active = (data.restoredData ?? []).filter(p => p.isActive)
const premium = active.some(p => p.entitlementId === 'premium')
}
A user who subscribes on the web and later installs the app finds their entitlement already there, because both paths key off the same user ID.
The DIY route, and what it costs
The standard advice is Capacitor: wrap the app yourself, open Xcode and Android Studio, and own the whole project. That ownership is real, and if you have native developers it is a legitimate route.
The costs land on everyone else. A Mac, provisioning profiles, and signing certificates on the iOS side. Billing and push arrive as plugins you integrate and maintain. Over-the-air updates lost their default answer when Ionic discontinued Appflow in February 2025. And offline, Capacitor serves your app from the file:// protocol, which breaks auth flows, OAuth redirects, and most SDKs that expect an http origin.
Despia is a native runtime instead of a project generator, and it gives you two deployment models. The default, remote hydration, ships the binary without embedded assets and loads the current build from your URL on every launch, which is exactly what makes over-the-air updates automatic. For offline-first apps, the Local Server option caches the full asset graph on-device and serves it from http://localhost, a real secure origin, so Service Workers, IndexedDB, and auth all keep working with no network. Either way the app runs in the platform WebView (WKWebView on iOS, Chromium-based WebView on Android) with 50+ native device features reachable through a single JavaScript function. One codebase, and the web app stays the source of truth.
Guideline 4.2, and what reviewers actually look for
Apple rejects apps that offer nothing beyond the website. A bare wrapper is exactly what 4.2 describes. The fix is real native behavior, one call away from the code you already have:
if (isDespia) {
// haptic feedback on key actions
despia('successhaptic://')
// biometric-gated storage via the vault
await despia('setvault://?key=confirmAction&value=yes&locked=true')
// register the device for push, linked to your user
despia(`setonesignalplayerid://?user_id=${userId}`)
}
Push notifications alone change the review conversation, and they change the product. Reaching users between sessions is usually the reason to have a mobile app at all. OneSignal is built into every Despia app, as is AppsFlyer for install attribution, so you can see whether a signup came from a TikTok ad or organic search without adding any custom native code.
Despia and DIY Capacitor, side by side
| Despia | DIY Capacitor | |
|---|---|---|
| In-app purchases | Built in via RevenueCat | Plugin, integrated by you |
| Push notifications | Built in via OneSignal | Plugin, integrated by you |
| Install attribution | Built in via AppsFlyer | Not included |
| Offline support | Full, via localhost server | file://, breaks auth and SDKs |
| OTA updates | Free, no MAU limits | Appflow discontinued (Feb 2025) |
| Deployment | One click, no Xcode | Xcode and Android Studio, yours |
Publishing without a Mac, and updating without a resubmission
Despia builds, signs, and submits from the browser. No Xcode, no Android Studio, no Mac, no CLI. You still need your own Apple Developer account ($99 per year) and Google Play Console account ($25 one-time), because the app ships under your name, but the build pipeline is not your problem. The signed iOS build uploads to App Store Connect automatically for TestFlight and submission, and for Google Play, Despia generates the AAB the store requires plus an APK for direct device testing.
After launch, the split is simple. Web changes ship over the air the moment you deploy, with no store review and no MAU-based pricing on updates. A store resubmission is only needed when the native layer itself changes, like a new app icon or a new permission. And if you ever outgrow the managed path, the full Xcode and Android Studio projects export at any time. Your app is never locked inside the platform.
When you should rebuild native instead
Some apps should not be web apps in a shell of any kind. Heavy 3D or AR, games, and products whose entire interface depends on platform-specific UI belong in SwiftUI and Kotlin, and a rebuild is the honest answer there.
For the other 90%, the product is the web app you already run, and the mobile version needs billing that passes review, push that reaches users, native behavior a reviewer can feel, and updates that do not wait on Apple. That is a runtime problem, not a rewrite problem.
FAQ
Will Apple reject my app as just a website?
It will if the app adds nothing beyond the site. Guideline 4.2 is the most cited risk for converted web apps, and the answer is native capability: push, haptics, biometrics, and native-gated features, all of which your web code reaches through despia() calls.
Do I need a Mac to publish to the App Store?
Not with Despia. Building, signing, and the App Store Connect upload run from the browser. You need an Apple Developer account, nothing more.
Can I get an APK to test on Android?
Yes. Despia builds an APK for direct device installs and the AAB that Google Play requires for submission. Google no longer accepts APKs for new store submissions, so you need both, for different jobs.
Can I keep using Stripe?
On the web, yes. Inside the app, digital content must go through Apple and Google billing, which is what the built-in RevenueCat integration handles. Same user ID on both paths, so entitlements follow the account.
Do I resubmit to the stores for every deploy?
No. Web deploys reach installed apps over the air on next launch. Resubmit only when the native layer changes.
Which frameworks does this work with?
Anything that serves a URL over HTTPS. React, Next, Vue, Svelte, plain HTML, or apps built in Lovable, Base44, Bolt, and other AI builders. If it runs in a mobile browser, it ships.
Get it on the stores
Take the web app you already have and ship it to iOS and Android without a CLI or a Mac. Code signing and submission run from the browser, and your web app stays the single source of truth.





