Rork で作ったアプリをストアに出して、最初に取りこぼしていたのがプッシュ通知の許可でした。起動した瞬間に OS の「"〇〇"は通知を送信します。よろしいですか?」が出る作りにしていたのですが、初回起動でいきなりこれを見せられたユーザーの多くが、何も考えずに「許可しない」を押していたのです。
厄介なのは、iOS では一度「許可しない」を押されると、アプリ側からはもうダイアログを出せなくなることです。あとから通知の価値が伝わっても、ユーザーは設定アプリを自分で開いて切り替えるしかありません。そこまでする人はほとんどいません。つまり、最初の一回の出し方を間違えると、そのユーザーへの通知チャネルを永久に失います。私は個人開発で壁紙系と癒し系のアプリを長く運用するなかで、この「最初の一回」を OS 任せにしないだけで、許可率が体感で大きく変わることを学びました。今回はその設計をまとめます。
なぜ「起動直後にいきなり」が一番もったいないのか
OS のプッシュ許可ダイアログには、開発者がコントロールできない制約が二つあります。ひとつは文面を変えられないこと。もうひとつは、ボタンが「許可」「許可しない」の二択で、後者を押されたら終わりだということです。
初回起動の瞬間は、ユーザーがまだアプリで何も体験していないタイミングです。価値を感じる前に「通知を送っていいか」と聞かれても、判断材料がありません。人は迷ったとき、より安全に見える「許可しない」を選びます。これはユーザーが冷たいのではなく、文脈がないところで二択を迫られているから起きる、ごく自然な反応です。
ここで効くのが事前許諾(pre-permission priming)です。OS のダイアログを出す前に、自前の画面で「なぜ通知が役に立つのか」を一度だけ説明し、ユーザーが「うん、欲しいかも」と思った状態にしてから OS のダイアログにつなぐ。この自前画面は何度でも出せますし、文面も自由です。ここで前向きになった人にだけ OS ダイアログを見せれば、「許可しない」の一発退場を大幅に減らせます。
設計の核心は「OS ダイアログを最後の確認に格下げする」こと
考え方をひっくり返すのがポイントです。OS のダイアログを「最初の関門」にするのではなく、「すでに気持ちが固まった人への最後の確認」に位置づけます。
実際の流れはこうです。まず自前の事前許諾画面で価値を伝える。そこで「あとで」を選んだ人には OS ダイアログを出しません(出してしまうと貴重な一回を消費するからです)。「通知を受け取る」を選んだ人にだけ OS ダイアログを出す。こうすると、OS ダイアログを見る時点でユーザーはすでに前向きなので、ほぼ通過します。
この記事でいちばんお伝えしたいのは、ここで二段階に分けることの本質は「許可率の数字を上げる小細工」ではない、という点です。本質は、ユーザーが断る自由を自前画面の「あとで」に逃がしてあげることです。OS のダイアログで断られると永久に再挑戦できませんが、自前画面の「あとで」なら、次にもっと良い文脈が来たときにまた出せます。断られ方をリカバリ可能な場所に移す、というのが設計の肝です。
Expo での実装
Rork は内部的に Expo(React Native)でアプリを生成します。プッシュ許可は expo-notifications で扱えます。まず、許可をリクエストする処理を、自前画面の「通知を受け取る」ボタンからだけ呼ぶようにします。
下のコードは、現在の許可状態を確認し、まだ一度も聞いていない場合にだけ OS ダイアログを出すヘルパーです。すでに denied の人には OS ダイアログを出さず、設定アプリへの導線を返すようにしています。
import * as Notifications from 'expo-notifications';
import { Linking } from 'react-native';
type PrimeResult = 'granted' | 'denied' | 'needs-settings';
// 自前の事前許諾画面で「通知を受け取る」が押されたときに呼ぶ
export async function requestPushAfterPriming(): Promise<PrimeResult> {
const current = await Notifications.getPermissionsAsync();
// すでに許可済みなら何もしない
if (current.status === 'granted') {
return 'granted';
}
// iOS で一度 denied になっていると requestPermissionsAsync は
// ダイアログを出さず即 denied を返す。設定アプリへ誘導するしかない
if (current.status === 'denied' && !current.canAskAgain) {
return 'needs-settings';
}
// まだ未確定(undetermined)のときだけ OS ダイアログを出す
const result = await Notifications.requestPermissionsAsync();
return result.status === 'granted' ? 'granted' : 'denied';
}
// 設定アプリを開く(needs-settings のとき)
export function openNotificationSettings() {
Linking.openSettings();
}ここで重要なのは canAskAgain の判定です。iOS では一度断られると status は denied、canAskAgain は false になり、requestPermissionsAsync() を呼んでもダイアログは出ません。これを知らずに「許可されていないからもう一度頼もう」と単純にリクエストを繰り返すコードを書くと、ユーザーには何も表示されないのに内部的には毎回 denied が返ってくる、という無言の失敗に陥ります。私自身、最初はこれに気付かず「なぜ許可率が上がらないのか」と悩みました。
呼び出し側の事前許諾画面は、たとえば次のような構成にします。価値を一文で示し、「通知を受け取る」と「あとで」の二択を置くだけのシンプルな画面です。
import { View, Text, Pressable } from 'react-native';
import { requestPushAfterPriming } from './push';
export function PushPrimingScreen({ onDone }: { onDone: () => void }) {
const handleAllow = async () => {
const result = await requestPushAfterPriming();
// granted / denied のいずれでも、ここから先は通常フローへ進める
onDone();
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 24 }}>
<Text style={{ fontSize: 22, fontWeight: '700', marginBottom: 12 }}>
新しい壁紙が届いたらお知らせします
</Text>
<Text style={{ fontSize: 15, lineHeight: 22, color: '#555', marginBottom: 32 }}>
週に数回、編集部が選んだ新作だけをそっとお届けします。頻度は設定からいつでも変えられます。
</Text>
<Pressable onPress={handleAllow} style={{ backgroundColor: '#111', padding: 16, borderRadius: 12, alignItems: 'center' }}>
<Text style={{ color: '#fff', fontWeight: '600' }}>通知を受け取る</Text>
</Pressable>
<Pressable onPress={onDone} style={{ padding: 16, alignItems: 'center', marginTop: 8 }}>
<Text style={{ color: '#888' }}>あとで</Text>
</Pressable>
</View>
);
}「あとで」を押した場合は requestPushAfterPriming を呼ばないことに注目してください。ここで OS ダイアログを出さないからこそ、後日もう一度チャンスがあります。
いつ出すか — タイミングがコピーより効く
文面を磨くより先に効くのが、出すタイミングです。私の経験では、起動直後ではなく「ユーザーがそのアプリで一度よい体験をした直後」に出すのが、いちばん通ります。
壁紙アプリなら、最初の一枚を実際に端末に設定し終えた直後。癒し系アプリなら、最初のセッションを再生し終えて「気持ちよかった」と感じている瞬間。つまり、通知の価値がその場の体験と地続きになるタイミングを狙います。「新作が届いたらまたこの体験ができますよ」という文脈が、ユーザーの頭の中で自然につながるからです。
逆に避けたいのは、起動直後・課金導線の直後・エラー直後の三つです。起動直後は文脈がなく、課金直後は「また何か要求された」と感じさせ、エラー直後はそもそも印象が悪いからです。
断られたあとのリカバリ
自前画面で「あとで」を選んだ人には、回数を決めて再提案します。私は「3回まで、それぞれ十分に間隔を空けて」を目安にしています。出すたびに同じ画面ではなく、その時々で実際に起きたこと(「あなたが保存した作品の続編が公開されました」など)を理由にすると、押し付けがましさが減ります。
OS レベルで denied になってしまった人には、アプリ内のどこか目立たない場所に「通知をオンにする」導線を一つだけ置き、押したら Linking.openSettings() で設定アプリを開きます。ここで大切なのは、しつこく出さないことです。一度 OS で断った人に何度も設定を促すのは、体験を損なうだけで効果も薄いというのが、運用してきた実感です。
通知は、うまく設計すればリテンションを支える静かな味方になりますが、出し方を間違えると最初の一回で関係が終わってしまう繊細なチャネルでもあります。OS のダイアログを最後の確認に格下げし、断る自由を自分の画面の中に用意しておく。この一手間だけで、取りこぼしはかなり減らせるはずです。同じところでつまずいている方の参考になれば幸いです。