[iOS] ローカル通知について

2016.02.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

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];

このコードが初めて実行された時、通知を許可するかどうかのダイアログが表示されます。 001

ここで許可を得られれば、やっとアプリは通知を行うことができるようにります。 許可(不許可)の内容は、アプリの設定に記憶され、事後、アプリ側からこれを変更することはできません。

002

003

(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];

通知は、画面がロック状態の時、次の様に表示されます。

004

また、ロック状態以外では、次の様に 画面上部から降りてきます。

006

それでは、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分経過しました。";

007

008

ロック中に通知を受けた際に、「スライドで」の後ろの部分に表示されるテキスト

@property(nonatomic, copy) NSString *alertAction

notification.alertAction = @"アプリに戻る";

005

通知をタップした時にアプリを起動するかどうか

@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;

009

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)の追加や、ユーザからの利用許可などが必要ですが、詳しくは、下記の記事をご参照ください。

[iOS] 位置情報の取得

010

所定の権限等がクリアできれば、UILocalNotificationregionに登録するだけで、位置情報からの通知が可能になります。 下記のコードは、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のシュミレータで擬似的に「東京駅」に位置情報を設定して動作確認しています。

012

8 最後に

今回は、ローカル通知について、まとめてみました。

非常に便利ですが、バックグラウンド状態にある場合や、停止している場合は、確実に起動されるとは限りませんので、その辺、ようく考えて設計する必要がありますね。

なお、arertTitleの使い所が、今ひとつ分かりませんでした・・・

9 参考資料

Local および Push Notification プログラミングガイド