こんにちは。VOYAGE GROUP で主に iOS アプリの開発をしている Swift はまだ様子見の @qmihara です。

iOS 8 では Push 通知に選択肢を付けられるようになりますね。
実際にどのようにすれば良いのか気になったので試してみました。

tech_blog-push_001
 
尚、本記事に記載されている内容は一般公開されているものをもとに記述しております。
画像につきましても一般公開されているものを引用しております。

環境

  • Xcode 6 beta 6
  • iOS 8 beta 5
  • Parse
お手軽に Push 通知を行うため Parse を使用しました。
便利です。

Push 通知の設定

(Push 通知の証明書の発行は従来と変わりありませんのでここでは省略します。)

iOS 8 から Push 通知の利用許可を求める方法が変わりました。
これまで `- registerForRemoteNotificationTypes:` でデバイストークンの取得、利用許可を求めるアラートの表示が行われていましたが、iOS 8 からはそれぞれ異なるメソッドを呼び出す必要があります。
[[UIApplication sharedApplication] registerForRemoteNotifications]; // <- Push 通知の利用登録(デバイストークンの発行)

UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings]; // <- 利用許可を求めるアラートの表示
`- registerForRemoteNotifications` の結果は、従来と同様成功時は `- application:didRegisterForRemoteNotificationsWithDeviceToken:` が、失敗時は `- application:didFailToRegisterForRemoteNotificationsWithError:` が呼ばれます。

`- registerUserNotificationSettings:` の結果は新たに `- application:didRegisterUserNotificationSettings:` メソッドが呼ばれます。
引数の `notificationSettings` に実際にユーザーが許可した内容が反映されています(Alert のみ、等)。

選択肢の設定

選択肢の設定は Push 通知の利用許可を求める際に併せて行います。
選択肢はアクションという単位で作成することになり、複数のアクションは一つのカテゴリに属することになります。
カテゴリは複数定義することが可能で、Push 通知を送る際に定義済みのカテゴリを指定することになります。

以下はカテゴリ、アクションの作成例となります。
UIMutableUserNotificationAction *acceptAction = [UIMutableUserNotificationAction new];
acceptAction.identifier = @"ACCEPT_IDENTIFIER";
acceptAction.title = @"Accept";
acceptAction.activationMode = UIUserNotificationActivationModeBackground;
acceptAction.destructive = NO;
acceptAction.authenticationRequired = NO;

UIMutableUserNotificationAction *maybeAction = [UIMutableUserNotificationAction new];
maybeAction.identifier = @"MAYBE_IDENTIFIER";
maybeAction.title = @"Maybe";
maybeAction.activationMode = UIUserNotificationActivationModeBackground;
maybeAction.destructive = NO;
maybeAction.authenticationRequired = NO;

UIMutableUserNotificationAction *declineAction = [UIMutableUserNotificationAction new];
declineAction.identifier = @"DECLINE_IDENTIFIER";
declineAction.title = @"Decline";
declineAction.activationMode = UIUserNotificationActivationModeBackground;
declineAction.destructive = YES;
declineAction.authenticationRequired = NO;

UIMutableUserNotificationCategory *inviteCategory = [UIMutableUserNotificationCategory new];
inviteCategory.identifier = @"INVITE_CATEGORY";
[inviteCategory setActions:@[acceptAction, maybeAction, declineAction] forContext:UIUserNotificationActionContextDefault];
[inviteCategory setActions:@[acceptAction, declineAction] forContext:UIUserNotificationActionContextMinimal];

NSSet *categories = [NSSet setWithObjects:inviteCategory, nil];
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:categories];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
 カテゴリは `UIMutableUserNotificationCategory`、アクションは `UIMutableUserNotificationAction` クラスを使用します。
アクションは識別子やタイトルなど必要な情報を設定します。

  • identifier
    • アクションを表す識別子
  • title
    • バナーやアラートに表示されるボタンタイトル
  • activationMode
    • Background、または Foreground を指定
    • Background であればアプリが起動することなく処理され、Foreground であればアプリが起動されます
  • destructive
    • ボタンタイトルを強調するかどうか
    • 削除など取り返しのつかない処理などで YES にする
  • authenticationRequired
    • YES を指定すると選択時にパスコードをの入力を求める
    • (iOS 8 beta 5 の時点では YES/NO に関わらず動きの違いはありませんでした。)
カテゴリには定義したアクションを設定するのですが、コンテキスト別でアクションを出し分けることができます。
コンテキストには以下の 2 種類があります。
  • UIUserNotificationActionContextDefault
    • ダイアログ表示時
    • 表示可能なアクションは最大 4 個
  • UIUserNotificationActionContextMinimal
    • バナー表示時
    • 表示可能なアクションは最大 2 個

選択肢付き Push 通知を送る

選択肢付きの Push 通知はペイロードに定義済みのカテゴリを指定することで送信することができます。
以下はカテゴリを指定したときのペイロード例です。
{
  "aps": {
    "alert": "Invitation from Jane Appleseed",
    "category": "INVITE_CATEGORY"
  }
}
tech_blog-push_002

tech_blog-push_003

次にアプリ側での通知の受け取り方です。
通知の選択肢がタップされた場合、`- application:handleActionWithIdentifier:forRemoteNotification:completionHandler:` メソッドが呼び出されます。
メソッドには定義したアクションの識別子が渡されますので、識別子に応じた処理を行うことになると思います。
最後に `completionHandler` を呼び出すのをお忘れなく。
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler {
    if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"]) {
        // ...
    } else if ([identifier isEqualToString:@"MAYBE_IDENTIFIER"]) {
        // ...
    }


    if (completionHandler) {
        completionHandler();
    }

}
選択肢ではなくバナー自体やダイアログの開くをタップした場合は、iOS 7 から追加された `- application:didReceiveRemoteNotification:fetchCompletionHandler:` が呼び出されます。
これまでの `- application:didReceiveRemoteNotification:` は呼ばれなくなっているのでご注意ください。

おまけ

選択肢付きの通知は Local Notification でも利用できます。

送り方は `UILocalNotification` に `category` プロパティが追加されていますので、ここにカテゴリの識別子をセットします。
UILocalNotification *localNotification = [UILocalNotification new];
localNotification.alertBody = @"Local notification";
localNotification.category = @"INVITE_CATEGORY";
[application scheduleLocalNotification:localNotification];
受け取りは `- application:handleActionWithIdentifier:forLocalNotification:completionHandler:` メソッドが呼ばれます。
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {

    if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"]) {
        // ...
    } else if ([identifier isEqualToString:@"MAYBE_IDENTIFIER"]) {
        // ...
    }
    if (completionHandler) {
        completionHandler();
    }
}

まとめ

Push 通知に選択肢を付けられるようになったことで、通知からアプリを開く以外に任意の動作を行わせることができるようになりました。
例えば簡単なアンケートであれば Push 通知から答えてもらうなどもできそうですね。(Yes or No)

しかしバナー表示の場合は選択肢があることに気付きにくいので(通知を引っ張る、スワイプ)、アプリ内のチュートリアルなどで選択肢付きの Push 通知が届くこと、選択肢はどのようにして表示させるのか、ユーザーがわかるように伝える必要がありそうですね。

今回使用したサンプルコードは AppDelegate だけですが gist で公開しております。
Xcode で Single View Application でも何でもプロジェクトを作成し、AppDelegatem.m にコピペしてもらえればと思います。
Parse を使用していますので、アプリケーション ID などは別途設定してください。