ダッシュボードを二つ並べて、私は自分のアプリが壊れていると思い込んでいました。Firebase 側の Day30 継続率は4%台。RevenueCat 側の継続率は78%。同じアプリの同じ月の数字です。
しばらく、後者の計測が間違っていると疑っていました。実際に間違っていたのは、二つを同じ物差しの上に並べた私の読み方のほうでした。片方はインストールした全員を分母にしていて、もう片方は課金を始めた人だけを分母にしています。重なる部分はありますが、別の質問に答えている数字です。
この取り違えは、そのまま LTV の計算に流れ込みます。分母の違う継続率を一つの式に入れると、出てくる金額は数倍ずれます。ここでは、その二つを分けて置き直すところから、Rork で作ったアプリの継続率と LTV の設計を組み直していきます。
二つの「継続率」を、同じ物差しで見ていた
まず、混ざりやすい二つを並べます。
—
インストール継続率
サブスクリプション継続率
分母
その日にインストールした全ユーザー
その月に課金を開始したユーザー
数える対象
X日後にアプリを起動したか
次の請求日に更新されたか
主な出所
Firebase Analytics / App Store Connect
RevenueCat / App Store Connect の購読レポート
典型的な水準
Day30 で 4〜8%
月次で 70〜85%
改善に効く打ち手
オンボーディング・習慣化・通知
価格・解約導線・請求失敗の回収
数字の桁が違うのは当然でした。インストール継続率は「無料で入ってきた人が居続けるか」を測っていて、サブスク継続率は「お金を払うと決めた人が払い続けるか」を測っています。後者の母集団は、前者をくぐり抜けた後の人たちです。
実務上いちばん困るのは、LTV の式に後者を入れるべき場面で前者を入れてしまうことです。私はこれをやりました。Day30 が4%だからと平均継続月数を極端に短く見積もり、「サブスクは自分のアプリには向いていない」という結論を一度出しています。実際には、課金まで到達した人の継続は悪くありませんでした。向いていなかったのはサブスクではなく、課金に至るまでの導線のほうでした。
インストール継続率の数字を、どこまで信じるか
業界平均としてよく引かれる範囲は、Day1 が 25〜35%、Day7 が 10〜15%、Day30 が 4〜8%、Day90 が 2〜5% あたりです。1,000人が入れて3ヶ月後に残るのは20〜50人という計算になります。
ただ、この範囲はカテゴリでかなり動きます。毎日開く前提のアプリと、必要なときだけ開くツールを同じ物差しで並べても意味がありません。壁紙のように「気が向いたときに開く」性質のアプリは、Day7 が平均を下回っていても収益が立つことがあります。逆に習慣化を売りにしているアプリで Day7 が平均並みなら、それは設計が効いていない合図です。
私は平均との比較をやめて、自分のアプリの前月コホートとだけ比べるようにしました。他人の平均は改善の方向を教えてくれませんが、先月の自分は教えてくれます。
各指標が答えている質問を一言にすると、こうなります。
Day1 — 最初の数分で「自分向けだ」と思ってもらえたか
Day7 — 二度目に開く理由を用意できたか
Day30 — 生活のどこかに置き場所ができたか
Day90 — 飽きた後に戻ってくる仕掛けがあるか
LTV の式を、個人開発の実態に合わせて直す
サブスクリプションの LTV は、次の形で見積もります。
LTV = ARPU(1ユーザーあたり月次収益)× 平均継続月数
平均継続月数 = 1 ÷ 月次チャーン率
ここで使う月次チャーン率は、先ほどの表の右側、課金開始者を分母にしたほうです。月次チャーン率が20%なら平均継続月数は5ヶ月、月額300円のサブスクなら LTV は約1,500円。チャーン率を10%まで下げられれば平均継続月数は10ヶ月になり、LTV は約3,000円に届きます。チャーン率を半分にすると LTV が倍になる、という関係です。
ただし、この式には前提が一つ隠れています。毎月のチャーン率が一定であるという仮定です。実際の解約は初月に集中します。無料トライアルから自動更新に切り替わる最初の請求で大きく落ち、そこを越えた人はしばらく残る、という形が普通です。
そのため、私は初月とそれ以降を分けて置くようにしました。
初月通過率 = 初回請求が成功して2ヶ月目に入った人 ÷ 課金開始者
2ヶ月目以降の平均継続月数 = 1 ÷(2ヶ月目以降の月次チャーン率)
LTV ≒ ARPU ×(1 + 初月通過率 × 2ヶ月目以降の平均継続月数)
式が一段複雑になりますが、打ち手の置き場所がはっきりします。初月通過率が低いなら直すのは価格とトライアルの長さで、2ヶ月目以降が悪いなら直すのは価値提供の側です。一つの平均チャーン率にまとめてしまうと、この分岐が見えなくなります。
もう一つ、請求失敗による意図しない解約も初月通過率を押し下げます。カード期限切れや残高不足で更新に失敗したまま復帰しない分が、体感よりずっと多く含まれています。ここは猶予期間と請求リトライの設定で回収できる領域です。ペイウォールと課金基盤側の設計はRork Max × RevenueCat Paywalls SDK の実装ガイド にまとめています。
CAC に生成クレジットを乗せる — 2026年の原価の見方
LTV/CAC は3倍以上あれば健全、というのがよく使われる目安です。個人開発でこの式を使うとき、私はしばらく CAC を広告費だけで数えていました。広告を出していないので CAC はほぼゼロ、したがって何をしても健全、という都合のいい計算です。
実際には、広告を出さなくても原価は毎月発生しています。2026年8月時点で Rork の有料プランは月額 $25 から、Apple エコシステム向けのネイティブ Swift を出力する Rork Max は月額 $200 という水準です。いずれもクレジット消費型で、生成のやり直しが多い月は消費が速く進みます。料金体系と為替は動きますので、実際の判断前には公式の記載で確認していただければと思います。
この固定費を1本あたりに割ると、個人開発の原価はこう見えてきます。
同時に回しているアプリ本数
ツール月額を1本あたりに割った額
月額300円サブスクで必要な課金継続者数
1本
月額の全額
おおむね20人前後
3本
月額の約3分の1
おおむね7人前後
6本
月額の約6分の1
おおむね4人前後
数字そのものより、この表が突きつけてくる問いのほうが大事でした。本数を増やせば1本あたりの原価は下がりますが、増やした分だけ1本にかけられる時間が減ります。時間が減れば継続率の改善は止まります。
私はここで、次の一本を作る前に条件を置くことにしました。今動いている1本の Day7 が前月コホートを上回るまで、新規のアプリには生成クレジットを使わない、という条件です。窮屈な決め方ですが、これを置く前は「うまくいかない1本を放置して次を作る」を繰り返していました。原価は増え続け、どの1本も継続率の改善サイクルに入りませんでした。
新規と改善のどちらにクレジットを割くかは、収益ではなく継続率の傾きで決めています。傾きが上を向いている1本は、まだ伸ばす余地が残っている合図です。
Day1 を動かすのは、オンボーディングの短さではなかった
インストール直後の数分で、ユーザーは続けるか消すかをほとんど決めています。ここで用意すべきは説明ではなく、小さく成功した実感です。
私は最初、この時間を短くすればいいと考えて、オンボーディングの質問を3問から1問に削りました。完了率は上がりました。Day1 は動きませんでした。
削ったのは手間だけではなく、そのユーザーが何のために来たのかを知る手がかりでした。目的が分からないので初回のホーム画面は全員同じ内容になり、結果として誰にとっても少しずつ的外れな画面が出ていました。質問を2問に戻し、答えに応じて最初に見せる内容を変えたところで、ようやく数字が動きました。
短くするかどうかではなく、その質問が後の画面を変えるかどうかが分かれ目でした。答えを受け取っても何も変えないのなら、その質問は削るべきです。
// Rork で実装するオンボーディングの目的別ルーティング
import { useState } from 'react' ;
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native' ;
const OnboardingGoalScreen = ({ onSelect }) => {
const goals = [
{ id: 'habit' , label: '毎日の習慣をつくる' , icon: '🎯' },
{ id: 'learn' , label: '新しいスキルを学ぶ' , icon: '📚' },
{ id: 'track' , label: '記録・分析したい' , icon: '📊' },
];
return (
< View style = { styles.container } >
< Text style = { styles.title } >あなたの目標を教えてください</ Text >
< Text style = { styles.subtitle } >最初に表示する内容が変わります</ 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 >
);
};
// 選択結果は AsyncStorage に保存し、ホーム画面の初期コンテンツ生成に渡します。
// 期待する動作: 選んだ目標に応じてホーム画面の先頭カードが差し替わる。
// 差し替えを実装しないうちは、この質問自体を出さない判断のほうが 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' },
});
最初の価値体験に到達するまでの時間は、私が Day1 と並べて毎週見ている数字です。オンボーディング完了率だけを見ていると、完了したのに何も起きていない状態を見落とします。
オンボーディング完了率 — どこまで進んだか
ステップ別の離脱率 — どこで止まるか
最初の価値体験までの経過時間 — 完了の先で何かが起きたか
完了群と未完了群の Day1 差分 — その一連が本当に効いているか
Day7 と Day30 — 習慣のきっかけをどこに置くか
二度目に開く理由を用意できているかが Day7 に、生活のどこかに置き場所ができたかが Day30 に出ます。行動のループで言えば、きっかけ・行動・報酬の三点をアプリの中に組み込む作業です。
きっかけは、時間で置くか、行動の直後に置くか、状況に紐づけるかで性質が変わります。私は時間ベースの一律通知から、直前の行動の続きとして出す形へ寄せていきました。1回のセッションは2〜3分で完結する単位まで小さくしています。長い体験は、忙しい日に真っ先に飛ばされます。
報酬は連続日数の表示、バッジ、共有といった形が定番です。連続日数は強く効きますが、途切れた瞬間に離脱を生む副作用も持っています。私は途切れたときに沈黙するのではなく、途切れた記録をそのまま残して再開の導線だけを出す形にしました。
通知については、送るかどうかより送る前提の作り方のほうが効きました。初回起動で通知許可を求めるのをやめ、最初の価値体験を終えた直後に、何を送るかを具体的に示してから求めています。許可率は説明の丁寧さより、求めるタイミングで動きました。
内容と頻度の目安はこう置いています。
価値提供型 — 新しい内容が増えたことを知らせる
再開のきっかけ型 — 途切れた記録の再開を促す
社会的な文脈型 — 他のユーザーの動きを伝える
頻度は週2〜3回から始めて、開封率と通知解除率を並べて調整します。開封率が5%を割るか、解除率が2%を超えたら、頻度ではなく内容を疑うようにしています。頻度を落として解決した例より、内容を変えて解決した例のほうが私には多くありました。
削除されないための設計そのものについては削除されないアプリの継続率設計 に別途まとめています。
チャーンの兆候を、退会される前に拾う
解約やアンインストールの理由は、おおむね次のどれかに収まります。価値の実感が薄れた、操作のストレスが積み上がった、別のアプリに移った、生活のほうが変わった、価格に納得できなくなった。
このうち最初の二つは、退会の前に必ず兆候が出ます。私が見ているのは次のシグナルです。
直近の起動頻度が、その人自身の平常時から3割以上落ちている
通知の開封が続けて途切れている
アプリ内のエラー発生回数が増えている
課金の更新に一度失敗している
平常時を全体平均ではなくその人自身に取るのが要点でした。週1回しか開かない人にとっての週1回は正常で、毎日開いていた人の週1回は異常です。全体平均を基準にすると、前者に不要な通知を送り、後者を見逃します。
兆候を拾った後に送る内容は、割引よりも「戻ってくる理由」を優先しています。値引きは一度出すと平常価格に戻しにくく、価格への納得を自分で崩す面があります。
退会時の1〜2問のアンケートは、定性データとして手元に残る数少ない情報源です。選択肢は自分の仮説を並べるのではなく、自由記述を1つ混ぜておくと、想定していなかった理由が出てきます。
広告費ゼロで伸ばす — 招待とシェアの設計
個人開発で広告費をかけずに伸ばす経路は、実質的に三つです。招待、成果の共有、そして協力や競争の誘いです。
招待は両者に見返りがある形が基本で、招待コードは Rork と Supabase の組み合わせで素直に実装できます。成果の共有は、ユーザーが達成した内容そのものが宣伝物になる点が利点です。協力型は、誘い文句を自分で書かなくても自然な招待になります。
// 達成内容の共有ボタン
import { Share, TouchableOpacity, Text, StyleSheet } from 'react-native' ;
const AchievementShareButton = ({ streak , score }) => {
const handleShare = async () => {
try {
const result = await Share. share ({
message: `🎉 ${ streak }日連続達成!スコア${ score }点 \n https://apps.apple.com/jp/app/your-app` ,
title: '達成を共有' ,
});
if (result.action === Share.sharedAction) {
// 共有完了時の処理(ポイント付与など)
console. log ( '共有が完了しました' );
}
} catch (error) {
console. error ( '共有エラー:' , error);
}
};
return (
< TouchableOpacity style = { styles.shareButton } onPress = { handleShare } >
< Text style = { styles.shareButtonText } >
🎉 { streak } 日達成を共有する
</ Text >
</ TouchableOpacity >
);
};
// 期待する動作: タップでネイティブの共有シートが開く。
// 注意: Share.sharedAction は「共有シートを閉じた」に近い挙動で、
// iOS では共有先まで特定できません。付与するポイントは小さく設計しておくと安全です。
const styles = StyleSheet. create ({
shareButton: {
backgroundColor: '#4CAF50' ,
padding: 16 ,
borderRadius: 12 ,
alignItems: 'center' ,
},
shareButtonText: { color: '#fff' , fontSize: 16 , fontWeight: 'bold' },
});
拡散の勢いは K ファクターで見ます。
K ファクター = 1ユーザーあたりの招待送信数 × 招待の受諾率
1.0 を超えると広告費なしで増え続ける計算になりますが、個人開発でそこに届く例は多くありません。私は 0.3〜0.5 を最初の目標に置いています。0.3 でも、獲得したユーザーが3割増しになる意味は小さくありません。
計測イベントの設計 — 予約語で落ちていた logEvent
継続率の改善は、計測が正しいことが前提です。私はここで一度、静かに失敗しています。
課金完了のイベントを logEvent('purchase', ...) で送っていて、ダッシュボードに何も出ませんでした。バックエンド側には売上が立っているので、計測の実装を疑うまでに時間がかかりました。原因は単純で、purchase は Google Analytics の予約イベント名です。@react-native-firebase/analytics の logEvent は予約語を受け取ると例外を投げます。私はその呼び出しを try/catch で囲んでいたため、例外が握り潰されて何事もなく動いているように見えていました。
正しくは専用のメソッドを使います。
import analytics from '@react-native-firebase/analytics' ;
// 課金完了 — 予約イベントは専用メソッドを使う
await analytics (). logPurchase ({
currency: 'JPY' ,
value: 580 ,
items: [{ item_name: 'pro_monthly' , item_id: 'pro_monthly' }],
});
// カスタムイベントは logEvent で送る
await analytics (). logEvent ( 'onboarding_complete' , {
goal_type: selectedGoal, // 選ばれた目標
time_to_complete_seconds: elapsedSeconds, // 完了までの秒数
});
await analytics (). logEvent ( 'first_value_moment' , {
feature_name: 'first_streak' ,
day_since_install: daysSinceInstall,
});
await analytics (). logEvent ( 'notification_permission' , {
granted: true ,
});
// 期待する動作: Firebase コンソールの DebugView に4件が並ぶ。
// try/catch で囲むときは、握り潰さず必ずログに出しておきます。
計測が動き出したら、変更は一つずつ試します。Firebase Remote Config を組み込んでおくと、個人開発でも配信を切り替えながら比較できます。私が優先して試しているのは、オンボーディングの質問数、通知を出す時間帯と文面、有料プランを提示する日、そして価格です。
同時に二つ変えない、判断まで最低1〜2週間は待つ、母数が小さいうちはアプリ内よりストアページから試す。この三つを守るだけで、間違った結論を出す回数がはっきり減りました。
毎週見る数字は5つに絞っています。DAU/MAU、Day7 継続率、課金者の月次チャーン率、月次 ARPU、そして LTV とツール原価を含めた獲得コストの比です。増やすほど見なくなるので、この数を超えないようにしています。実測値ベースの損益の組み立ては月額100円アプリの黒字化の実測メモ も併せてご覧いただければと思います。
9月の配布要件が、継続率の前提を変える
継続率の話は、そもそもアプリを入れられることの上に乗っています。2026年9月に、その前提側で二つの締切が来ます。
一つは Android の開発者認証で、9月に完全義務化される予定です。Android 17 以降では認証が OS 側に実装され、認証を経ていないアプリは新規インストールが OS のレベルで止まりうる構造になります。もう一つは App Store で、新規申請・アップデート申請と代替配信向けの notarization の提出時に、追加の回答が必須になります。
一方で、iOS 27 SDK でビルドしたバイナリの提出必須化は2027年春の予定です。9月にまとめて慌てる必要はなく、急ぐものと急がないものを仕分けておけば足ります。
継続率の改善に取り組んでいる最中にこの種の要件を落とすと、積み上げた数字がまとめて無効になります。私は8月のうちに、自分の配布経路を一枚に書き出しました。どのストアに何本出していて、どれが認証の対象で、どのアカウントで提出しているか。書き出してみると、休眠させたまま放置していたアプリが数本出てきます。
日程と要件は変わりますので、判断の前には Apple と Google の開発者向けの告知で確認をお願いいたします。
今週、数字を見る前にやること
継続率の改善は、施策を増やすところからではなく、見ている数字が何を指しているかを確かめるところから始めるのが早道でした。順番はこうしています。
インストール継続率とサブスク継続率を別の場所に並べ直し、LTV の式に入れているのがどちらか確認する
logEvent の予約語を洗い出し、課金イベントが本当に記録されているかを DebugView で見る
今動いている1本の Day7 を前月コホートと比べ、下向きなら次のアプリの生成を止めて、そこに時間を戻す
3つ目がいちばん実行しにくく、いちばん効きました。新しいものを作っているときは前に進んでいる感覚がありますが、継続率の数字はその感覚とほとんど関係なく動きます。
マネタイズ全体の組み立てはRork 収益化マスタープラン 2026 にまとめています。継続率の設計と収益モデルは、どちらか片方だけでは動きません。ここまでお読みいただきありがとうございました。