[iOS] ローカル通知について
1 ローカル通知とは
フォアグラウンドで動作していないアプリがでも、ユーザに何らかの情報を伝えるために、「ユーザ通知」という仕組みが用意されています。
iOSにおける「ユーザ通知」には、ローカル通知とリモート通知(プッシュ通知)の2種類がありますが、今回は、このうちローカル通知についてまとめてみました。
2 通知の受信
(1) ユーザ側から見ると
アプリが、フォアグラウンドで動作していない場合、次のような形で通知が届きます。
- 画面上の警告やバナー
- アイコン上のバッジ
- 警告、バナー、バッジと共に鳴るサウンド
ユーザが、これらの通知に対してアクションすると、OSがアプリを起動します。
フォアグラウンドで動作中(ユーザと対話中)は、特に変化はありません。(通知の履歴には追加されています)
(2) アプリ側から見ると
フォアグラウンドで動作していない場合は、通知があってもアプリとしては何もできません。 ユーザが通知に対してアクションを起こし、OSがアプリを起動してはじめて、アプリは処理を実行することができるようにります。
通知を経由してOSから起動された場合、アプリの状態によって、通知オブジェクトを受け取る方法が異なりまが、これについては、後述致します。
3 ローカル通知の設定手順
ローカル通知を設定する一般的な手順は次の通りです。
- 通知タイプの登録
- 通知オブジェクトの生成と内容指定
- スケジューリング
それでは、順に確認してみます。
(1) 通知タイプの登録
ローカル通知を行う場合は、通知タイプの登録を行う必要があります。 この際、ユーザへの確認ダイアログが表示され、ユーザがこれを許可した場合のみアプリは通知を送ることができます。
通知のタイプは、次の3種類です。
- テキスト
- サウンド
- アイコンバッジ
通知タイプの登録を行うコードは、次の通りです。
// 許可をもらう通知タイプの種類を定義 UIUserNotificationType types = UIUserNotificationTypeBadge | // アイコンバッチ UIUserNotificationTypeSound | // サウンド UIUserNotificationTypeAlert; // テキスト // UIUserNotificationSettingsの生成 UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; // アプリケーションに登録 [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
このコードが初めて実行された時、通知を許可するかどうかのダイアログが表示されます。
ここで許可を得られれば、やっとアプリは通知を行うことができるようにります。 許可(不許可)の内容は、アプリの設定に記憶され、事後、アプリ側からこれを変更することはできません。
(2) 通知オブジェクトの生成と内容指定
UILocalNotificationに各種の項目を設定し、登録している一例は、次の通りです。
// インスタンス生成 UILocalNotification *notification = [[UILocalNotification alloc] init]; // 通知時間(5分後) notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:(60 * 5)]; // タイムゾーン notification.timeZone = [NSTimeZone defaultTimeZone]; // メッセージ notification.alertBody = @"5分経過しました。"; // 音 notification.soundName = UILocalNotificationDefaultSoundName; // 通知の登録 [[UIApplication sharedApplication] scheduleLocalNotification:notification];
通知は、画面がロック状態の時、次の様に表示されます。
また、ロック状態以外では、次の様に 画面上部から降りてきます。
それでは、UILocalNotificationに設定できる主なプロパティについて、見ていきます。
通知時間の設定
@property(nonatomic, copy) NSDate *fireDate
秒単位で指定します。過去を設定すると直ちに実行されてしまいます。
タイムゾーンの設定
@property(nonatomic, copy) NSTimeZone *timeZone
繰り返し周期の設定
@property(nonatomic) NSCalendarUnit repeatInterval
デフォルトは0(繰り返しなし) NSMinuteCalendarUnit(毎分)、NSHourCalendarUnit(毎時)、NSWeekCalendarUnit(毎日)、NSDayCalendarUnit(毎週)などが指定できます。
繰り返しのために参照するカレンダーの設定
@property(nonatomic, copy) NSCalendar *repeatCalendar
通知に表示する本文
@property(nonatomic, copy) NSString *alertBody notification.alertBody = @"5分経過しました。";
ロック中に通知を受けた際に、「スライドで」の後ろの部分に表示されるテキスト
@property(nonatomic, copy) NSString *alertAction notification.alertAction = @"アプリに戻る";
通知をタップした時にアプリを起動するかどうか
@property(nonatomic) BOOL hasAction
デフォルトはYES(アプリを起動する)。 NOにする場合は「alertBody」がnilでない必要があります
通知から起動した時のスプラッシュ画像(通常とは別のものが指定可能です)
@property(nonatomic, copy) NSString *alertLaunchImage
アプリに格納されている画像を指定します。
通知時のサウンド
@property(nonatomic, copy) NSString *soundName
アプリに格納されているサウンドファイルを指定します。 デフォルトはnilとなっているので、特に用意がない場合は、UILocalNotificationDefaultSoundNameを指定するのが無難かも知れません。
指定できるサウンドは、30秒以内です。これを超えた場合は、デフォルトのシステム音になってしまいます。
通知につける付加情報
@property(nonatomic, copy) NSDictionary *userInfo
検索などが必要な場合に設定します。
(3) スケジューリング
上記で作成したUILocalNotificationオブジェクトをscheduleLocalNotificationで登録します。
通知の登録
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
アプリケーション毎、64件までのローカル通知を設定できます。これを超えた場合は、直近の64件以外は無効になります。
4 バッヂ
こちらは、UILocalNotificationのプロパティではありません。
// アイコンの右上に表示するバッヂの値 @property(nonatomic) NSInteger applicationIconBadgeNumber; [UIApplication sharedApplication].applicationIconBadgeNumber = 5;
0を指定することで、表示中のバッヂを消すことができます。
5 通知の削除
不要になった通知は、アプリで削除する必要があります。
アプリから登録した全ての通知を削除する場合は、次のようになります。
[[UIApplication sharedApplication] cancelAllLocalNotifications];
特定の通知を削除したい場合は、検索して個別に削除することになりますが、この場合、検索が可能なように、あらかじめキーを指定しておく必要があります。
// 登録する前に、userInfoにキーを設定しておく notification.userInfo = [NSDictionary dictionaryWithObject:@"999" forKey:@"id"];
// アプリに登録されている通知を列挙する for(UILocalNotification *notification in [[UIApplication sharedApplication] scheduledLocalNotifications]) { // キーで検索 if([[notification.userInfo objectForKey:@"id"] integerValue] == 999) { // キーが一致した場合、削除する [[UIApplication sharedApplication] cancelLocalNotification:notification]; } } }
他のアプリが登録した通知は、削除はもちろん、列挙することもできません。
6 アプリ側での処理
アプリ側で通知を受け取る場合、次の3種類が考えられます。
- (1) フォアグラウンドで起動中
- (2) バックグラウンドで起動中(他のアプリがフォアグラウンドで起動していたり、ロック画面になっている場合など)
- (3) アプリが起動していない場合
そして、それぞれによって、通知から起動されたことを検出する方法が変わります。
まず、(1)フォアグラウンドで起動中と(2)バックグラウンドで起動中の場合は、didReceiveLocalNotificationが呼ばれます。 この中で、applicationState を確認することで、どの状態から呼び出されたのかを知ることができます。 ただし、バックグラウンドの場合、あくまで、ユーザが通知に対してアクションした時点です。 アクションが無ければ、呼び出されることはありません。
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { if(application.applicationState == UIApplicationStateActive) { NSLog(@"フォアグラウンドで起動中の場合"); } if(application.applicationState == UIApplicationStateInactive) { NSLog(@"バックグラウンドで起動中の場合"); } // 通常、削除の処理を行う [[UIApplication sharedApplication] cancelLocalNotification:notification]; }
続いて、(3)起動していない場合ですが、これは、普通にアプリが起動する場合に呼び出される、didFinishLaunchingWithOptionsで処理するしかありません。 単純に起動された場合と違って、通知経由でOSから起動された場合は、launchOptionsからUIApplicationLaunchOptionsLocalNotificationKeyで検索すると通知オブジェクトを取得することができます。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; // nilでなければ、通知の情報が格納されている if(notification != nil) { // 通常、削除の処理を行う [[UIApplication sharedApplication] cancelLocalNotification:notification]; } return YES; }
7 位置情報に基づく通知
位置情報を取得するには、フレームワーク(CoreLocation.framework)の追加や、ユーザからの利用許可などが必要ですが、詳しくは、下記の記事をご参照ください。
所定の権限等がクリアできれば、UILocalNotificationのregionに登録するだけで、位置情報からの通知が可能になります。 下記のコードは、CLLocationManagerDelegateのデリゲートメソッドで、kCLAuthorizationStatusAuthorizedAlwaysの状態になった時点で、通知を設定している例です。
- (void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { if(status == kCLAuthorizationStatusAuthorizedAlways){ // 目的地の設定 CLLocationCoordinate2D target = CLLocationCoordinate2DMake(35.681507, 139.765581); // 東京駅 CLLocationDistance radius = 500.0; // 半径何メートルで通知するか CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:target radius:radius identifier:@"identifier"]; // インスタンス生成 UILocalNotification *notification = [[UILocalNotification alloc] init]; // メッセージ notification.alertBody = @"目的地に到着しました。"; notification.regionTriggersOnce = false; notification.region = region; // 音 notification.soundName = UILocalNotificationDefaultSoundName; // 通知の登録 [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } }
実行している様子は次のとおりです。Xcodeのシュミレータで擬似的に「東京駅」に位置情報を設定して動作確認しています。
8 最後に
今回は、ローカル通知について、まとめてみました。
非常に便利ですが、バックグラウンド状態にある場合や、停止している場合は、確実に起動されるとは限りませんので、その辺、ようく考えて設計する必要がありますね。
なお、arertTitleの使い所が、今ひとつ分かりませんでした・・・