●PRICE — Rork Max spans $200 to $1,800 per month, with the upper tiers aimed at heavier builders and teams●FREE — The free tier lands at roughly five prompts per week, enough to try it but not to build on continuously●SHIP — App Store publishing is automated through builds, certificates, and submission, so you can ship an iOS app without a Mac or Xcode●SIM — A browser-streamed simulator lets you watch your app run in a real Apple environment from your own browser●NATIVE — It reaches HealthKit, ARKit and LiDAR, NFC, Dynamic Island, and Metal 3D — territory React Native cannot touch●FUNDING — Rork raised a $15M seed led by Left Lane Capital, announced April 9, 2026, and acquired app builder Paperline●PRICE — Rork Max spans $200 to $1,800 per month, with the upper tiers aimed at heavier builders and teams●FREE — The free tier lands at roughly five prompts per week, enough to try it but not to build on continuously●SHIP — App Store publishing is automated through builds, certificates, and submission, so you can ship an iOS app without a Mac or Xcode●SIM — A browser-streamed simulator lets you watch your app run in a real Apple environment from your own browser●NATIVE — It reaches HealthKit, ARKit and LiDAR, NFC, Dynamic Island, and Metal 3D — territory React Native cannot touch●FUNDING — Rork raised a $15M seed led by Left Lane Capital, announced April 9, 2026, and acquired app builder Paperline
Retention and LTV for Rork Apps — Separating the Two Numbers I Kept Confusing
Install retention and subscription retention use different denominators. Starting from the LTV miscalculation that came from mixing them, this walks through reading Day1/Day7/Day30 correctly, costing in generation credits, and catching churn signals early — from an indie developer's desk.
I had two dashboards open, and I was convinced my app was broken. Firebase showed Day30 retention hovering around 4%. RevenueCat showed retention at 78%. Same app, same month.
For a while I assumed the second number was instrumented wrong. What was actually wrong was my reading of them. One counts everyone who installed. The other counts only people who started paying. They overlap, but they answer different questions.
That mix-up flows straight into your LTV math. Put a retention rate with the wrong denominator into the formula and the number that comes out is off by a multiple. So let's start by pulling the two apart, then rebuild retention and LTV design for a Rork-built app on top of that.
The two retention numbers I was reading on one scale
Here are the two side by side.
—
Install retention
Subscription retention
Denominator
Everyone who installed on a given day
Everyone who started paying in a given month
What it counts
Did they open the app X days later?
Did the next billing cycle renew?
Usual source
Firebase Analytics / App Store Connect
RevenueCat / App Store Connect subscription reports
Typical range
4–8% at Day30
70–85% monthly
What moves it
Onboarding, habit formation, notifications
Price, cancel flow, failed-payment recovery
The order-of-magnitude gap makes sense once you see it. Install retention measures whether people who arrived for free stick around. Subscription retention measures whether people who already decided to pay keep paying. The second group is what's left after surviving the first.
The practical damage shows up in the LTV formula. I dropped install retention into a slot that wanted subscription churn, computed an absurdly short average lifetime, and concluded that subscriptions didn't suit my app. In reality, the people who reached checkout stuck around fine. What didn't work wasn't the subscription — it was the path leading up to it.
How much to trust install retention benchmarks
The commonly cited ranges land near 25–35% at Day1, 10–15% at Day7, 4–8% at Day30, and 2–5% at Day90. Out of 1,000 installs, roughly 20 to 50 people are still active three months later.
Those ranges swing hard by category, though. An app you're meant to open daily and a tool you open only when you need it don't belong on the same scale. A wallpaper app that people open when the mood strikes can earn well with a below-average Day7. An app whose entire pitch is habit formation sitting at exactly average is telling you the design isn't landing.
I stopped comparing against industry averages and started comparing only against my own prior-month cohort. Someone else's average can't tell me which direction to move. Last month's me can.
Boiled down, each milestone answers one question:
Day1 — did the first few minutes feel like it was built for this person?
Day7 — was there a reason to come back a second time?
Day30 — did the app find a slot in someone's routine?
Day90 — is there anything that brings people back after the novelty fades?
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦The install-retention vs subscription-retention mix-up that broke my LTV math, and the step-by-step way to pull the two apart
✦How to load tool subscriptions and generation credits into the C of LTV/CAC, plus the rule I use to decide between building the next app and fixing the current one
✦A silent analytics failure caused by a reserved event name in @react-native-firebase/analytics, and the correct way to log purchase events
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Rebuilding the LTV formula around how indie apps actually behave
Subscription LTV is usually estimated like this:
LTV = ARPU (monthly revenue per user) × average months retained
average months retained = 1 ÷ monthly churn rate
The churn rate here is the right-hand column above — paying users as the denominator. A 20% monthly churn rate gives an average of 5 months, so a ¥300/month subscription implies roughly ¥1,500 of LTV. Get churn to 10% and the average doubles to 10 months, putting LTV near ¥3,000. Halve churn, double LTV.
There's a hidden assumption in that formula, though: that churn stays flat month over month. Real cancellations cluster in the first period. There's a steep drop at the first charge after a trial converts, and the people who clear that tend to stay for a while.
So I split the first period out from the rest.
first-period pass rate = users whose first charge succeeded and entered month 2 ÷ users who started paying
average months after month 2 = 1 ÷ (monthly churn rate from month 2 onward)
LTV ≈ ARPU × (1 + first-period pass rate × average months after month 2)
The formula gets one step heavier, but it tells you where to aim. A weak first-period pass rate points at pricing and trial length. Weak month-2-onward retention points at the value you're delivering. Collapse both into a single average churn rate and that fork disappears.
Involuntary churn drags the first-period number down too. Expired cards and insufficient funds fail renewals, and a lot of those people never come back — more than it feels like from the inside. Grace periods and billing retry settings recover a real share of it. I've written up the paywall and billing side separately in the Rork Max × RevenueCat Paywalls SDK implementation guide.
Loading generation credits into your CAC
An LTV/CAC ratio above 3 is the usual health benchmark. For a long time I computed CAC as ad spend only. I don't run ads, so CAC was roughly zero, so everything I did was healthy by definition. Convenient math.
The costs were there regardless. As of August 2026, Rork's paid tier starts around $25/month, and Rork Max — which outputs native Swift for the Apple ecosystem — sits at $200/month. Both run on credit consumption, so a month with a lot of regeneration burns through faster. Pricing and exchange rates move, so please confirm against the official listing before making a call on it.
Amortize that fixed cost across your apps and the real unit economics surface:
Apps running in parallel
Tool cost per app
Paying subscribers needed at ¥300/month
1
The full monthly fee
Roughly 20
3
About one third
Roughly 7
6
About one sixth
Roughly 4
The numbers matter less than the question the table forces. More apps lower the per-app cost, but every additional app takes time away from the others. Less time per app means retention work stops.
So I set a condition: no generation credits go toward a new app until the one I'm currently running beats its own prior-month Day7. It's a restrictive rule. Before I set it, I was repeatedly abandoning an app that wasn't working in order to start the next one. Costs kept climbing and not one of them ever entered a real improvement cycle.
When I decide between new work and improvement, I look at the slope of retention rather than revenue. An app whose retention is trending up still has room left in it.
What actually moved Day1 wasn't a shorter onboarding
People mostly decide within the first few minutes whether to keep an app. What belongs in that window isn't explanation — it's the feeling of having succeeded at something small.
My first move was to shorten it. I cut onboarding from three questions to one. Completion rate went up. Day1 didn't move.
What I'd cut wasn't just friction. It was the only signal telling me why this person showed up. Without that, the first home screen was identical for everyone, which meant it was slightly wrong for everyone. Restoring a second question and changing what appears first based on the answer is what finally moved the number.
The dividing line isn't short versus long. It's whether the question changes a later screen. If you collect an answer and change nothing, cut the question.
// Goal-based routing in a Rork onboarding flowimport { View, Text, TouchableOpacity, StyleSheet } from 'react-native';const OnboardingGoalScreen = ({ onSelect }) => { const goals = [ { id: 'habit', label: 'Build a daily habit', icon: '🎯' }, { id: 'learn', label: 'Learn a new skill', icon: '📚' }, { id: 'track', label: 'Track and analyze', icon: '📊' }, ]; return ( <View style={styles.container}> <Text style={styles.title}>What are you here for?</Text> <Text style={styles.subtitle}>This changes what you see first</Text> {goals.map((goal) => ( <TouchableOpacity key={goal.id} style={styles.goalButton} onPress={() => onSelect(goal.id)} > <Text style={styles.goalIcon}>{goal.icon}</Text> <Text style={styles.goalLabel}>{goal.label}</Text> </TouchableOpacity> ))} </View> );};// Persist the selection to AsyncStorage and pass it into home screen generation.// Expected behavior: the first card on the home screen swaps based on the choice.// Until that swap is implemented, dropping the question entirely did more for Day1.const styles = StyleSheet.create({ container: { flex: 1, padding: 24, backgroundColor: '#fff' }, title: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 }, subtitle: { fontSize: 16, color: '#666', marginBottom: 32 }, goalButton: { flexDirection: 'row', alignItems: 'center', padding: 16, marginBottom: 12, borderRadius: 12, backgroundColor: '#f5f5f5', }, goalIcon: { fontSize: 24, marginRight: 12 }, goalLabel: { fontSize: 16, fontWeight: '500' },});
Time-to-first-value is the number I watch alongside Day1 every week. Watching completion rate alone hides the state where someone finished onboarding and nothing happened afterward.
Onboarding completion rate — how far people get
Per-step drop-off — where they stop
Time to first value moment — whether anything happened past completion
Day1 gap between completers and non-completers — whether the flow is doing any work at all
Day7 and Day30 — where to place the trigger
Day7 reflects whether you gave someone a reason to open the app twice. Day30 reflects whether the app found a slot in their week. In behavioral terms, you're building a cue, a routine, and a reward into the product.
Cues behave differently depending on whether you anchor them to a clock, to the action just completed, or to context. I moved away from blanket time-based notifications toward cues that continue whatever the person was already doing. Sessions are sized to finish in two or three minutes — long experiences are the first thing skipped on a busy day.
Rewards usually take the form of streaks, badges, or sharing. Streaks work, but they carry a side effect: the moment one breaks, people leave. Instead of going quiet when a streak breaks, I leave the record visible and surface only the path back in.
For notifications, how I set up permission mattered more than what I sent. I stopped asking on first launch. Now the request comes right after the first value moment, with a concrete description of what will be sent. Opt-in rate moved with timing, not with how carefully the request was worded.
The content types I keep in rotation:
Value delivery — something new is available
Restart nudge — pick up an interrupted record
Social context — what other users are doing
I start at two or three sends per week and tune against open rate and opt-out rate together. If open rate drops under 5% or opt-outs pass 2%, I question the content before the frequency. Changing what I send has fixed more cases for me than sending less.
Reasons for cancelling and uninstalling mostly fall into five buckets: the value stopped being felt, UX friction accumulated, a competitor won them over, their life changed, or the price stopped making sense.
The first two always show signals beforehand. What I watch:
Launch frequency down 30%+ from that person's own baseline
Consecutive missed notification opens
Rising in-app error counts
One failed billing renewal
Taking the baseline from the individual rather than the population is the key part. Once a week is normal for someone who has always opened it once a week, and alarming for someone who used to open it daily. A population average sends unnecessary nudges to the first group and misses the second entirely.
When I do reach out, I lead with a reason to return rather than a discount. Discounts are hard to walk back, and offering one undercuts the price you just asked someone to accept.
A one- or two-question exit survey is one of the few qualitative sources you get for free. Rather than listing only your own hypotheses, leave one free-text field in — that's where the reasons you never considered show up.
Growing without ad spend — referrals and sharing
There are effectively three organic paths for an indie app: referrals, achievement sharing, and collaborative or competitive invites.
Referrals work best with a reward on both sides, and invite codes are straightforward to build with Rork plus Supabase. Achievement sharing has the advantage that the user's own result becomes the ad. Collaborative invites work because the user writes the pitch for you.
// Achievement share buttonimport { Share, TouchableOpacity, Text, StyleSheet } from 'react-native';const AchievementShareButton = ({ streak, score }) => { const handleShare = async () => { try { const result = await Share.share({ message: `🎉 ${streak}-day streak, ${score} points\nhttps://apps.apple.com/app/your-app`, title: 'Share achievement', }); if (result.action === Share.sharedAction) { // Post-share handling (award points, etc.) console.log('Share completed'); } } catch (error) { console.error('Share error:', error); } }; return ( <TouchableOpacity style={styles.shareButton} onPress={handleShare}> <Text style={styles.shareButtonText}> 🎉 Share your {streak}-day streak </Text> </TouchableOpacity> );};// Expected behavior: tapping opens the native share sheet.// Caveat: sharedAction is closer to "the sheet was dismissed" than a confirmed share,// and iOS won't tell you the destination. Keep any reward you grant small.const styles = StyleSheet.create({ shareButton: { backgroundColor: '#4CAF50', padding: 16, borderRadius: 12, alignItems: 'center', }, shareButtonText: { color: '#fff', fontSize: 16, fontWeight: 'bold' },});
Spread is measured with the K-factor:
K-factor = invites sent per user × invite acceptance rate
Above 1.0 you grow without ad spend, but very few indie apps get there. I aim for 0.3 to 0.5 first. Even 0.3 means every acquisition brings 30% more with it.
Analytics design — the logEvent that was failing on a reserved name
Retention work assumes your measurement is correct. I got that wrong once, quietly.
I was sending purchase completions through logEvent('purchase', ...) and nothing appeared in the dashboard. Revenue was landing on the backend, so it took me a while to suspect the instrumentation. The cause was simple: purchase is a reserved Google Analytics event name, and logEvent in @react-native-firebase/analytics throws when handed a reserved name. I had wrapped the call in a try/catch, so the exception was swallowed and everything looked fine.
The reserved events have dedicated methods.
import analytics from '@react-native-firebase/analytics';// Purchases — use the dedicated method for reserved eventsawait analytics().logPurchase({ currency: 'JPY', value: 580, items: [{ item_name: 'pro_monthly', item_id: 'pro_monthly' }],});// Custom events still go through logEventawait analytics().logEvent('onboarding_complete', { goal_type: selectedGoal, // which goal was chosen time_to_complete_seconds: elapsedSeconds, // seconds to finish});await analytics().logEvent('first_value_moment', { feature_name: 'first_streak', day_since_install: daysSinceInstall,});await analytics().logEvent('notification_permission', { granted: true,});// Expected behavior: four events appear in the Firebase DebugView.// If you wrap these in try/catch, log the error rather than swallowing it.
Once measurement works, change one thing at a time. Firebase Remote Config makes flag-based comparison practical even for a solo developer. My priority list is onboarding question count, notification timing and copy, the day the paid plan is offered, and price.
Never change two things at once, wait at least one to two weeks before judging, and while volume is low, test on the store page rather than inside the app. Those three rules alone cut down how often I reached a wrong conclusion.
I keep the weekly dashboard to five numbers: DAU/MAU, Day7 retention, monthly churn among paying users, monthly ARPU, and LTV against an acquisition cost that includes tooling. Any more than that and I stop looking. For a worked example built on measured figures, see breaking even on a ¥100/month app.
September's distribution requirements change the baseline
All of this sits on top of people being able to install your app at all. Two deadlines land on that foundation in September 2026.
The first is Android developer verification, scheduled to become fully mandatory. From Android 17 onward, verification is implemented at the OS level, and unverified apps can be blocked from new installs by the operating system itself. The second is App Store submission: new submissions, updates, and notarization for alternative distribution will require additional answers.
Meanwhile, the requirement to submit builds compiled against the iOS 27 SDK is scheduled for spring 2027. There's no need to rush everything into September — you just need to sort what's urgent from what isn't.
Dropping a requirement like this in the middle of a retention push invalidates the numbers you've been accumulating. In August I wrote my distribution footprint onto a single page: which stores hold which apps, which fall under verification, and which account submits them. Writing it out surfaced several dormant apps I'd left sitting.
Dates and requirements shift, so please confirm against Apple's and Google's developer announcements before acting on them.
Before you look at the numbers this week
Retention work goes faster when you start by confirming what your numbers point at, rather than by adding tactics. My order:
Put install retention and subscription retention in separate places, then check which one is feeding your LTV formula
Audit your logEvent calls for reserved names and verify in DebugView that purchase events are actually recording
Compare the current app's Day7 against its own prior-month cohort — if it's trending down, stop generating the next app and give that time back
The third is the hardest to follow and the one that mattered most. Building something new feels like forward motion, but retention numbers move almost independently of that feeling.
The full monetization picture is laid out in the Rork monetization master plan 2026. Retention design and revenue model don't move on their own — they need each other. Thanks for reading this far.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.