コンテンツにスキップ

02. Push通知を実装してアクティブ率を向上させる

Webアプリと比較したときのモバイルアプリ最大の強み、それは「ユーザーのスマートフォンの画面に直接Push通知を届けられること」です。

ユーザーが希望する時間のリマインダーは、記録を思い出す助けになります。ただし、継続率が一定の倍率で伸びるとは限りません。通知の許可率・解除率と、翌日(D1)・7日後(D7)の継続率を計測して効果を確かめます。

この記事では、Expo公式の通知ライブラリ「expo-notifications」を使って、端末内で完結するローカル通知、アプリアイコンのバッジ管理、そしてサーバーから配信するリモートプッシュ通知を実装する手順を詳しく解説します。


  1. Push通知の種類:ローカル通知 vs リモート通知の使い分け
  2. expo-notificationsの導入と権限リクエストの実装
  3. 毎日決まった時間にリマインドするローカル通知の実装
  4. 未完了タスク数をアプリアイコンに表示するバッジ管理
  5. 通知タップ時のディープリンク(画面遷移)制御
  6. EAS Push / FCMを用いたリモートプッシュ通知の概要
  7. まとめと次のステップ

1. Push通知の種類:ローカル通知 vs リモート通知の使い分け

Section titled “1. Push通知の種類:ローカル通知 vs リモート通知の使い分け”

モバイルアプリの通知には2つの方式があります。

  • ローカル通知(Local Notifications):
    • 端末自身がタイマーをセットし、指定時刻(例: 毎日21:00)に通知を発火する。
    • バックエンドサーバー不要、通信環境不要で動作するため、リマインダーや目覚ましに最適。
    • 初期の個人開発アプリなら、ローカル通知だけで十分すぎる効果を発揮します。
  • リモート通知(Remote Push Notifications):
    • サーバーから特定のユーザーや全ユーザーに向けて一斉送信する(例: お知らせ、友達からのメッセージ)。
    • APNs(Apple)やFCM(Firebase Cloud Messaging)との連携が必要。

2. expo-notificationsの導入と権限リクエストの実装

Section titled “2. expo-notificationsの導入と権限リクエストの実装”

まずは公式通知パッケージをインストールします。

Terminal window
npx expo install expo-notifications expo-constants expo-dev-client

iOSおよびAndroidで通知を送信するには、ユーザーの明示的な許可が必要です。

lib/notifications.ts
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
// アプリ起動中の通知表示設定(フォアグラウンドでもバナーを出す)
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
shouldShowList: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
export async function registerForPushNotificationsAsync(): Promise<boolean> {
// Android 13以降では、通知チャンネルを先に作る
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'Default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#10B981', // DESIGN.mdのPrimaryカラー
});
}
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
return false;
}
return true;
}

3. 毎日決まった時間にリマインドするローカル通知の実装

Section titled “3. 毎日決まった時間にリマインドするローカル通知の実装”

習慣管理アプリ(HabitFlow)において、最も効果的な「毎晩21:00のリマインダー」をスケジュールするコードです。

export async function scheduleDailyReminder(hour: number = 21, minute: number = 0) {
// このリマインダーだけを置き換え、他の予約通知は残す
await Notifications.cancelScheduledNotificationAsync('habitflow-daily-reminder');
// 毎日指定時刻に繰り返し通知
await Notifications.scheduleNotificationAsync({
identifier: 'habitflow-daily-reminder',
content: {
title: '🌟 今日の小さな一歩を記録しよう',
body: '今日の習慣を振り返って、できたことを記録しましょう。',
data: { screen: 'today' },
sound: true,
},
trigger: {
type: Notifications.SchedulableTriggerInputTypes.DAILY,
channelId: 'default',
hour,
minute,
},
});
}

設定画面で権限を確認し、許可された場合に予約します。OFFにしたら同じ識別子で予約をキャンセルします。端末の省電力設定やOSの制約で配信が遅れる場合があるため、正確な時刻の到達を保証する用途には別途検討が必要です。


4. 未完了タスク数をアプリアイコンに表示するバッジ管理

Section titled “4. 未完了タスク数をアプリアイコンに表示するバッジ管理”

アプリアイコンの右上に赤い数字(バッジ)を表示することで、ユーザーに「未完了のタスクがある」ことを直感的に思い出させます。

// 今日の未完了タスク数に合わせてバッジを更新
export async function updateAppBadge(uncompletedCount: number) {
if (Platform.OS !== 'web') {
await Notifications.setBadgeCountAsync(uncompletedCount);
}
}

ユーザーがすべてのタスクにチェックを入れた瞬間、updateAppBadge(0) を呼んでバッジを消去することで、爽快な達成感を提供できます。


5. 通知タップ時のディープリンク(画面遷移)制御

Section titled “5. 通知タップ時のディープリンク(画面遷移)制御”

ユーザーが通知バナーをタップした際、アプリが開くだけでなく「今日の記録画面」へ直接誘導することで、操作の離脱を防ぎます。

// app/_layout.tsx などに配置
import { useEffect } from 'react';
import * as Notifications from 'expo-notifications';
import { Slot, useRootNavigationState, useRouter } from 'expo-router';
function NotificationObserver() {
const router = useRouter();
const navigation = useRootNavigationState();
// 起動中のタップと、終了状態から通知で起動した場合を受け取る
const response = Notifications.useLastNotificationResponse();
useEffect(() => {
if (!navigation?.key || !response) return;
if (response.actionIdentifier !== Notifications.DEFAULT_ACTION_IDENTIFIER) return;
if (response.notification.request.content.data?.screen === 'today') {
router.push('/(tabs)');
Notifications.clearLastNotificationResponse();
}
}, [navigation?.key, response, router]);
return null;
}
export default function RootLayout() {
return <><Slot /><NotificationObserver /></>;
}

既存のルートレイアウトに認証やテーマのProviderがある場合は、それらを残して通知監視を追加します。通知内の任意のURLをそのまま開かず、許可した画面だけへ遷移します。


6. EAS Push / FCMを用いたリモートプッシュ通知の概要

Section titled “6. EAS Push / FCMを用いたリモートプッシュ通知の概要”

全ユーザーに向けた新機能告知や、友達同士のリアクション通知を送りたい場合は、Expoが提供する無料のプッシュサービス 「Expo Push API」 を活用します。

Expo Push Serviceを使う場合も、AndroidのFCM v1資格情報とiOSのAPNs認証キー等の設定が必要です。 EASはその管理を支援します。Notifications.getExpoPushTokenAsync({ projectId }) にEASのプロジェクトIDを渡してトークンを取得し、認証済みユーザーの端末情報として保存します。

サーバーからExpo Push APIへ送信した後は、送信チケットと配信レシートを確認し、DeviceNotRegistered などが返る無効なトークンを削除します。通知の配信は必ず成功するとは限りません。設定順序・資格情報はExpoの通知セットアップ、エラー処理は送信ガイドを参照してください。


プッシュ通知とバッジの実装によって、ユーザーがアプリを手放せなくなる強力なリテンションフックが完成しました。

次の記事では、Google Gemini APIを活用し、アプリ内にインテリジェントなAIチャットやユーザーに合わせた個別アドバイス機能を組み込む「03. AI機能(Gemini API)をアプリに組み込む」に進みましょう。