-
Notifications
You must be signed in to change notification settings - Fork 336
7.5 UILocalNotification
この節では、ユーザーに対して通知を行う "ローカル通知" を扱います。今までの節(NSNotification)は、プログラミングのメカニズムとしての通知を扱いましたが、ここでは画面上でユーザーに何か情報を提示する通知を扱います。
公式のリファレンスは Local およびPush Notificationプログラミングガイド をご覧ください。
ユーザーが受け取る通知にも通知の機構による違いや、表示できるコンテンツがあります。
機構ごとの違いとしては、
- サーバーから任意のタイミングで通知を送ることのできるリモート通知(Push通知とよく呼ばれています)
- ある時刻や、ある場所に入ったときに通知されるローカル通知 。どの条件で通知を出すかはプログラム内で指定します。
の二つがあります。どちらの通知もユーザーからの見た目は同じです。
通知の見た目などでカスタマイズすることのできるものは
- アプリアイコンの右上につくバッジの数
- 通知を出す時のサウンド
- 表示するメッセージ
です。iOS8からは通知に対するアクションの選択肢を表示できるオプションが追加されました。
Push通知はサーバーからユーザーの端末に通知を届けるメカニズムです。 Push通知はiOS Developer Programに加盟していることが条件になるためこのトレーニングコースでは取り上げませんが 全体の仕組みだけ少し紹介します。
詳しくは、冒頭であげたリファレンスを参考にしてください。
- 各アプリのサーバーからAppleの通知を受け取るサービス(Apple Push Notification Service: 通称APNs)のサーバーに通知のコンテンツを送信します
- APNsサーバーから各iOS端末に通知が送られます
- 各端末で通知が表示されます(場合によってはアプリ内でハンドリングを行います)
Figure 3-1 Pushing a remote notification from a provider to a client app
サーバーからPush通知を送る端末を指定するためにはデバイストークンを利用します。デバイストークンとはiOSから提供されるハッシュ値となり、 UIApplication#registerForRemoteNotifications
を呼んだ結果として得ることができます。
このデバイストークンを各アプリのバックエンドサーバーに保存しておき、通知を送るときに利用します。 デバイストークンは使用する度ごとに変わるものではなく、各端末、アプリごとに一意な値になっています。
APNsサーバーに通知を送信するには、 gateway.push.apple.com:2195 に向けて独自のバイナリプロトコルでTSLで送信します。 詳しいプロトコルの仕様などについては こちらをご覧ください。
この際、通知のコンテンツに加えて
- どの端末に送信するかを決めるためのデバイストークン
- どのアプリからのPush通知かを保証するための証明書(iOS Developer Centerより入手)
の2つを指定する必要があります。
このプロトコルを実装するのは骨が折れるので、外部サービスを利用したり、あるいはサードパーティのライブラリを利用することをおすすめします。例えばGemでは https://github.com/nomad/houstonなどがおすすめです。
Push通知を送った際に、ターゲットの端末の電源がOffになっていたり、圏外にいた場合は端末に到達することができません。 そのような場合、APNsでは再送を行いますが、一定量以上再送のキューが溜まった場合は古い通知から破棄されます。 そのため、Push通知は信頼性の高い通信プロトコルとして採用しないほうがよいでしょう。
UILocalNotification(ローカル通知)はサーバーを介さず、アプリから通知を送る方法です。通知は指定の時刻、あるいは指定の位置に入った時に行われます。 サーバーを介さないので何か他のユーザーのアクションを通知するといった用途には使えませんが、アプリ内で完結するので実装が比較的簡単です。
以下では、指定の時刻に通知を出す方法について解説をします。
ユーザーに通知を送る際には、通知を送ることをユーザーが許可している必要があります。そのためまずは、ユーザーの許諾を得ることが必要になります。
ユーザーの許諾を得るためには、iOS8以降では UIApplicationの - (void)registerUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
を実行します。
UIUserNotificationType types = UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettingssettingsForTypes:types
categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
ここで指定している UIUserNotificationType は通知で利用するタイプで、サウンド、バッジ、アラートの3つがあります。それぞれどの通知を送るかをオプショナルで指定します。 このタイプをUIUserNotificationSettingsで追加します。UIUserNotificationSettingsは通知をUIUserNotificationCategoryのカテゴリごとに分けるためのクラスです。カテゴリを分けることで通知から簡単なアクションを行うことができますが、ここではカテゴリを用いずnilを指定します。
このUIUserNotificationSettingsを引数として -registerUserNotificationSettings:
を実行することでユーザーの許諾を得ます。
このメソッドを実行すると
のようなアラートが表示されます。このアラートでユーザーが"OK"を押すと通知が許可されます。
許可された場合、appDelegateのコールバック関数 application:didRegisterUserNotificationSettings:
が呼ばれます。
アラートを表示→ユーザーがアラートをタップ→コールバック関数が呼ばれる のフェーズはそれぞれ非同期で行われるので、許諾を得てすぐ次の行で通知を登録する、ということはできません。
何かしらの方法で一度通知を待機させておく必要があります。
一度許諾を得てしまえば、改めて通知を登録するときに許諾を得る必要はなく初回の一度のみで十分です。 そこで、許諾済みかどうかをチェックし、許諾済みでない場合にのみ許諾を得るようにします。
許諾の状態の取得には UIApplicationの - (UIUserNotificationSettings *)currentUserNotificationSettings
を利用します。
このメソッドの返り値で得られるUIUserNotificationSettingsのtypesが利用できる通知のタイプになっており、UIUserNotificationTypeNone が通知の許諾がない状態となります。
この状態の時のみ、許諾を得るようにします。
UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if ( currentSettings.types == UIUserNotificationTypeNone ) {
// ここで許諾を得る
}
UIUserNotificationTypeNoneは通知できるタイプがない状態を表しますが、これは未許諾の状態と、ユーザーが許諾を断った時の両方で該当します。iOS8 SDKではこの二つの状態を区別するメソッドはないようです。
ですが、-registerUserNotificationSettings:
を実行してアラートが出るのは一度のみなので、何度もアラートが出るということはありません。
-registerUserNotificationSettings:
や - currentUserNotificationSettings
はiOS8SDK以降で利用できるメソッドになり、iOS7以前では許諾を得る必要がありません。
そのためこれらのメソッドを実行する必要はなく、むしろ実行してしまうとunrecognized selectorの例外が発生してクラッシュしてしまいます。そのため以下のようにメソッドがあるかどうかをチェックする必要があります。
if ( [[UIApplication sharedApplication] respondsToSelector:@selector(currentUserNotificationSettings)] ) {
// iOS8SDK以降でこの場合のみ許諾を得る
} else {
// iOS7SDK以前なので許諾を得る必要はない
}
許諾を得ることができたら、通知を登録します。通知の登録は、UILocalNotificationインスタンスを作り、UIApplicationの- scheduleLocalNotification:
を実行するだけです。
以下が実行のサンプルです。
// 通知のインスタンスを作成
UILocalNotification *notification = [[UILocalNotification alloc] init];
// 通知を表示する時刻
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60*3]; // 3分後
// アラートの本文
notification.alertBody = @"This is Local Notification";
// 通知で鳴らすサウンド. アプリ内に予めインストールしておいたサウンドのファイル名を指定
notification.soundName = @"sound_file_name";
// アプリアイコンにつけるバッジの数
notification.applicationIconBadgeNumber = 3;
// アプリケーション独自に通知に関する情報をセットすることが出来る
notification.userInfo = @{@"some_id": @"foobar"};
// 通知を登録する
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
UILocalNotificationの各プロパティの詳細については リファレンス を参照して下さい。
通知を受けてアプリケーションを起動した時に、どのような通知から起動したかを判断し、ハンドリングすることができます。 AppDelegateの特定のメソッドがコールバック関数として実行されるのですが、アプリケーションの状態を応じて呼ばれるコールバック関数が異なります。
注意
通知経由の起動時のコールバック関数はドキュメントで明記されておらず、手元の環境(iOS SDK 8.3)で実行した結果になります。 実装する場合は実際に動作を検証して下さい。
アプリケーションプロセスが実行中の場合、つまりアプリを利用中(フォアグラウンドにいる)の場合と、アプリがバックグラウンドの場合は、
- application:didReceiveLocalNotification:
が呼ばれます。
引数には起動したUILocalNotificationが含まれています。
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
// 通知登録時の情報を利用したい場合はuserInfoより取り出す
NSDictionary *userInfo = notification.userInfo;
if (application.applicationState == UIApplicationStateActive) {
// アプリが起動中の場合の場合のハンドリングをここに記述する
// 通知が表示された時、即座に実行される
} else if (application.applicationState == UIApplicationStateBackground) {
// アプリが起動中のバックグラウンドの場合のハンドリングをここに記述する
// 表示されたときではなく、通知をタップしたり、スワイプして起動した時に実行される
}
}
アプリケーションプロセスがない場合、つまりバックグラウンドでも動いていない時に通知をタップしたり、スワイプで起動した場合はアプリケーションプロセスの起動が行われ、そのコールバック関数の引数に通知の情報が含まれます。
アプリケーション起動時のコールバック関数は - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
です。(- application:didFinishLaunchingWithOptions:
も呼び出されます。) この引数の launchOptions
は起動時のオプションなのですが、 UIApplicationLaunchOptionsLocalNotificationKey
で取り出される値が通知のUILocalNotificationインスタンスになります。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 中略
UILocalNotification *notification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
if (notification != nil) {
// ローカル通知経由の場合の処理をここで行う
}
return YES;
}
既に登録済みの通知を取得するには
[[UIApplication sharedApplication] scheduledLocalNotifications];
で取り出すことができます。(リファレンス) また、ある通知を削除する場合は
[[UIApplication sharedApplication] cancelLocalNotification:notification];
で削除します。(リファレンス)
はじめに
-
導入
-
1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)
-
UIKit 1 - container, rotate-
-
UIKit 2- UIView -
-
UIKit 3 - table view -
-
UIKit 4 - image and text -
-
ネットワーク処理
-
ローカルキャッシュと通知
-
Blocks, GCD
-
設計とデザインパターン
-
開発ツール
-
テスト
-
In-App Purchase
-
付録