LASSIC Media らしくメディア
アプリのローカル通知の実装
LASSIC IT事業部|元請(プライムベンダー)としてシステム保守・運用を受託
この記事のポイント
- ローカル通知はサーバーを介さず端末内でOSがスケジュールする通知で、リマインダーや定時通知に向いています。
- iOSとAndroidでは許可要求のタイミング・トリガの種類・OSバージョンごとの権限要件が異なります。
- Android 13以降のPOST_NOTIFICATIONS権限や正確なアラームの制約など、バージョン差分の実装コストを外注/内製の判断軸として整理します。
目次
ローカル通知とは何か、プッシュ通知との違い
モバイルアプリのローカル通知とは、サーバーを経由せず、端末内でOSがあらかじめ指定した時刻や条件に基づいてスケジュールし発火させる通知を指します。リマインダーアプリの「毎朝8時に服薬を通知する」機能や、カレンダーアプリの「予定30分前に知らせる」機能がこれに当たります。
本稿で扱うのは、この端末内完結型のローカル通知です。サーバーから配信するリモートのプッシュ通知(FCM・APNsを使った配信基盤やセグメント配信、MAU改善施策)は別の仕組みであり、別稿で扱います*1。また、通知の許諾率やオプトイン率をどう高めるかというテーマも別稿の範囲です*2。本稿はあくまで「OSが端末内でスケジュールして発火する通知」の実装論点に絞って解説します。
ローカル通知はネットワーク接続が不要なため、オフライン環境でも動作する点が利点です。一方で、iOSとAndroidではAPIの設計思想・権限要件・OSバージョンごとの制約が大きく異なり、両OS対応には個別の実装知識が必要です。
iOSのローカル通知実装 — UNUserNotificationCenterとトリガ
iOSのローカル通知は、UserNotificationsフレームワークのUNUserNotificationCenterを中心に実装します*3。アプリはまずrequestAuthorization(options:)を呼び出し、アラート・サウンド・バッジ表示などの許可をユーザーに求めます*4。この許可要求は非同期処理であり、ユーザーが拒否した場合は通知そのものが表示されません。
許可取得後は、通知の内容を表すUNMutableNotificationContentと、発火条件を表すUNNotificationTriggerを組み合わせてUNNotificationRequestを作成し、addNotificationRequest(_:withCompletionHandler:)で登録します*5。トリガには主に3種類があります。
UNCalendarNotificationTriggerは、年月日や時分など任意の日時要素を指定して通知を発火させる仕組みで、「毎日8時30分に通知する」といった定時リマインダーに使います*6。UNTimeIntervalNotificationTriggerは、登録時点から一定秒数が経過した後に発火する仕組みで、タイマー的な通知に向いています*7。UNLocationNotificationTriggerは、指定した地理的領域への入退場を条件に通知を発火させる仕組みです*8。位置情報の利用にはCore Locationの権限が別途必要になるため、リマインダーアプリで地点通知を実装する場合は権限設計が二重に発生する点に注意が必要です。
Androidのローカル通知実装 — チャネルとスケジューリング
Androidでは通知そのものはNotificationとNotificationManagerで表示しますが、いつ発火させるかのスケジューリングはAlarmManagerやWorkManagerが担います*9。まず前提として、Android 8.0(API26)以降はすべての通知にNotificationChannelの割り当てが必須です*10。チャネルにはID・ユーザーに見える名前・重要度(IMPORTANCE_HIGHからIMPORTANCE_NONEまでの5段階)を設定し、createNotificationChannelで登録します。一度作成したチャネルの音や表示挙動はアプリ側から変更できず、最終的な制御はユーザーの端末設定に委ねられます。
正確な時刻に通知を発火させたい場合はAlarmManagerのsetExactAndAllowWhileIdleなどを使い、指定時刻にブロードキャストを受け取ってNotificationを表示する構成が一般的です*9。一方、時刻の厳密さよりバッテリー消費を優先してよい定期処理には、WorkManagerのPeriodicWorkRequestが適しています*11。WorkManagerは充電中のみ実行するなどの制約(Constraints)を付けられる反面、最小間隔が15分に制限されるため、分単位の精密なリマインダーには向きません*11。
どちらの手段を選ぶかは、通知の性質によって決まります。服薬リマインダーのように時刻の正確性が重要な通知はAlarmManager、集計結果の定期通知のように多少の遅延を許容できる処理はWorkManagerという使い分けが実務上の基本方針になります。
Android 13以降の権限変更が実装に与える影響
Android 13(API33)以降を対象にするアプリは、通知を送信するためにPOST_NOTIFICATIONSランタイム権限を宣言し、ユーザーから許可を得る必要があります*10。マニフェストにandroid.permission.POST_NOTIFICATIONSを記載したうえで、実行時にユーザーへ許可ダイアログを表示する実装が必須になった点が、Android 12以前との大きな違いです。
Android 13以上の端末にアプリを新規インストールした場合、通知は既定でオフの状態になり、この権限が許可されるまで通知は一切表示されません*10。既存の通知チャネルを持ち、かつAndroid 12L以前で通知を明示的に無効化されていないアプリは、自動的に権限が事前付与される救済措置がありますが、新規アプリはこの対象になりません。
この変更により、Android 13以降を対象にしたアプリでは、リマインダー機能を使い始める前にユーザーへ許可を求める導線設計が不可欠になりました。iOSのrequestAuthorizationと同様、「いつ・どの画面で許可ダイアログを出すか」がリマインダー機能の利用開始率を左右する設計ポイントになります。
正確なアラームの制約と代替設計
Android 12(API31)では、正確なアラームを扱うSCHEDULE_EXACT_ALARM権限が導入されました*12。Android 12でこの権限を宣言したアプリは自動的に許可される扱いでしたが、Android 13を対象にしたアプリでは新規インストール時に自動付与されなくなり、Android 14ではさらに扱いが厳格化されています*13。
カレンダーアプリや目覚まし時計アプリなど、正確な時刻通知が本質的な機能であるアプリ向けには、インストール時に自動許可されるUSE_EXACT_ALARM権限が用意されています*12。ただし、この権限は該当するアプリカテゴリ以外での利用が想定されておらず、一般的な業務アプリのリマインダー機能で使う場合はSCHEDULE_EXACT_ALARMの方が実装上の前提になります。
実装時はcanScheduleExactAlarmsで権限の有無を確認し、許可されていない場合はACTION_REQUEST_SCHEDULE_EXACT_ALARMのインテントでユーザーを設定画面に誘導する必要があります*12。また、ユーザーが権限を許可・剥奪した際にはACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGEDのブロードキャストが送られるため、権限状態の変化を検知してアラームの再設定や通知の見直しを行う設計が求められます*13。この権限が得られない前提での代替設計(許容誤差のある通知への切り替えなど)をあらかじめ用意しておくことも、リリース後の権限拒否リスクに備えるうえで重要です。
iOSとAndroidの実装差分がもたらす外注/内製の判断軸
ここまで見てきたように、ローカル通知とは、端末内でOSが時刻や位置などの条件を監視し、条件成立時にサーバーを介さず通知を発火させる仕組みです。iOSとAndroidでは権限モデル・トリガの種類・スケジューリング手段が異なるため、両OS対応には個別の設計と検証が必要になります。
両OSの主な違いを整理すると次のとおりです。iOSはUNUserNotificationCenter配下に統合されたAPI体系を持ち、トリガの種類ごとに許可要求の仕組みは共通です。Androidは通知の表示(NotificationManager・チャネル)とスケジューリング(AlarmManager・WorkManager)が別々のAPIに分かれており、さらにOSバージョンごとに権限要件が積み重なっている点が特徴です。
| 比較項目 | iOS | Android |
|---|---|---|
| 通知の許可要求 | UNUserNotificationCenterのrequestAuthorization(options:)でアラート・サウンド・バッジ等を一括要求。 | Android 13以降はPOST_NOTIFICATIONSランタイム権限が必須。 Android 12以前は権限宣言不要。 |
| 通知チャネル | 通知チャネルの概念はなし。 カテゴリ(UNNotificationCategory)でアクションを分類。 |
API26以降は全通知にNotificationChannelの割り当てが必須。 重要度をチャネル単位で設定。 |
| 日時指定トリガ | UNCalendarNotificationTriggerで年月日・時分を柔軟に指定。 | AlarmManagerのsetExactAndAllowWhileIdle等で時刻指定。 Android 12以降はSCHEDULE_EXACT_ALARM等の権限が必要。 |
| 経過時間トリガ | UNTimeIntervalNotificationTriggerで登録後の経過秒数を指定。 | WorkManagerのPeriodicWorkRequest(最小間隔15分)または通常のAlarmManager。 |
| 位置トリガ | UNLocationNotificationTriggerで地理的領域の入退場を条件化。 Core Locationの権限が別途必要。 |
標準APIに専用の位置トリガはなく、GeofencingClient等と組み合わせて自前実装する。 |
この表からわかるとおり、Androidは通知チャネル・正確なアラーム権限・POST_NOTIFICATIONSという3層の権限/設定管理が必要で、OSバージョン分岐のテストパターンが多くなります。自社に両OSのネイティブ実装経験者がいない場合、あるいはFlutter・React Nativeなどクロスプラットフォーム基盤を使っていてもOS固有のプラグイン挙動差を検証する工数が確保できない場合は、外部の開発パートナーへの委託を検討する判断軸になります。逆に、既存アプリに単純な定時リマインダーを1機能追加する程度であれば、両OSの公式ドキュメントに沿った内製実装でも対応できる範囲だと考えられます。
失敗しやすい落とし穴と設計時の留意点
ローカル通知の実装でよくある失敗の一つが、許可ダイアログのタイミング設計です。アプリ起動直後にいきなり許可を求めると、機能の価値を理解する前にユーザーが拒否してしまい、一度拒否された許可は端末設定からの手動変更が必要になります。この設計を誤ると、リマインダー機能そのものが使われないまま放置されるリスクがあります。
Androidの正確なアラーム権限も見落とされやすい落とし穴です。SCHEDULE_EXACT_ALARMが許可されていない状態でsetExact系のAPIを呼び出すとSecurityExceptionが発生する場合があり*12、権限確認を怠るとアプリクラッシュにつながります。canScheduleExactAlarmsでの事前チェックと、権限が得られない場合の代替フロー(許容誤差のある通知に切り替える、設定画面へ誘導するなど)を用意しないまま公開すると、リリース後に不具合報告が集中する事態になりかねません。
実装に必要な知識領域も単純ではありません。iOS側はSwift/ObjC+UserNotificationsフレームワークの理解、Android側はKotlin/Java+AlarmManager・WorkManager・通知チャネルの理解に加え、OSバージョンごとの権限分岐を網羅したQA体制が必要です。目安として、iOS・Android双方でリマインダー機能を新規実装し主要バージョンでの動作検証まで行う場合、モバイルエンジニア1〜2名体制での対応が想定されますが、対象OSバージョンの範囲や既存アプリとの統合難易度によって必要工数は変動します。自社の体制でこの検証工数を確保できるかどうかが、外注を検討する現実的な判断基準になります。
まとめ:ローカル通知実装の3つの判断軸
本稿では、端末内でOSがスケジュールして発火するローカル通知について、プッシュ通知との違いからiOS・Androidそれぞれの実装方式、OSバージョンごとの権限変更までを整理しました。要点を3つに集約すると次のとおりです。第一に、ローカル通知はサーバー不要でオフラインでも動作する一方、iOSとAndroidでAPI体系と権限モデルが大きく異なります。第二に、Android 13のPOST_NOTIFICATIONSやAndroid 12以降の正確なアラーム権限など、OSバージョンごとの権限変更に追随した実装と検証が欠かせません。第三に、両OSの実装・検証工数を自社で確保できるかどうかが、内製と外注を分ける現実的な判断軸になります。
よくある質問
ローカル通知とプッシュ通知はどちらも実装すべきですか。
アプリの目的によって使い分けが必要です。リマインダーや定時通知のようにサーバーからの情報を必要としない機能はローカル通知で完結できます。一方、キャンペーン告知や個別ユーザー向けの配信はサーバー経由のプッシュ通知が必要になり、両方を実装するアプリも珍しくありません。プッシュ通知の実装詳細は別稿で解説しています。
Android 13以降で通知が届かないという相談が増えていますが原因は何ですか。
最も多い原因は、POST_NOTIFICATIONSランタイム権限をユーザーが許可していないことです*10。Android 13以降を対象にした端末では通知が既定でオフになるため、アプリ側で許可ダイアログを表示し、ユーザーが許可するまで通知は一切表示されません。
正確な時刻に通知したい場合、AlarmManagerとWorkManagerのどちらを使うべきですか。
時刻の正確性を優先する場合はAlarmManager、多少の遅延を許容できる定期処理はWorkManagerが適しています*9*11。WorkManagerのPeriodicWorkRequestは最小間隔が15分に制限されるため、分単位の厳密なリマインダーには向きません。
既存アプリに通知機能を後から追加する場合、どの程度の改修規模になりますか。
既存アプリのアーキテクチャや対象OSバージョンの範囲によって規模は変わります。単純な定時通知の追加であれば影響範囲は限定的ですが、Android側で通知チャネルや権限要求の導線を新規に組み込む場合はUI変更を伴うことがあります。既存コードベースの構成を確認したうえで見積もる必要があります。
位置情報を使ったリマインダー通知は両OSで同じように実装できますか。
同じようには実装できません。iOSはUNLocationNotificationTriggerという専用のトリガが用意されていますが*8、Androidの標準通知APIには同等の専用トリガがなく、位置情報の監視の仕組みと組み合わせて自前で実装する必要があります。加えて両OSとも位置情報の利用許可を別途取得する設計が必要です。
著者:テレリモ総研編集部 鈴木 亮佑
ご不明な点はお問い合わせフォームからもご連絡いただけます。
- *1 参考:プッシュ通知(リモート配信)の実装・活用は別稿で解説しています。
- *2 参考:通知の許諾率・オプトイン改善は別稿で解説しています。
- *3 出典:Apple Developer Documentation「UNUserNotificationCenter」
- *4 出典:Apple Developer Documentation「Asking permission to use notifications」
- *5 出典:Apple Developer Documentation「Scheduling a notification locally from your app」
- *6 出典:Apple Developer Documentation「UNCalendarNotificationTrigger」
- *7 出典:Apple Developer Documentation「UNTimeIntervalNotificationTrigger」
- *8 出典:Apple Developer Documentation「UNLocationNotificationTrigger」
- *9 出典:Android Developers「Schedule alarms」
- *10 出典:Android Developers「Notification runtime permission」
- *11 出典:Android Developers「Getting started with WorkManager」
- *12 出典:Android Developers「Schedule alarms(正確なアラーム権限)」
- *13 出典:Android Developers「Schedule exact alarms are denied by default」