iOS8からのプッシュ通知：選択肢付きのお知らせ機能

iOS8からプッシュ通知でユーザが選択肢を選べるようになりました。
プッシュ通知に選択肢を追加する方法をまとめました。

![プッシュ通知iOS8.jpg](https://qiita-image-store.s3.amazonaws.com/0/34488/1b11e087-739b-5b08-db88-0708ee76ccfa.jpeg)

(本記事の内容は[一般公開されているiOS8の情報](https://developer.apple.com/library/prerelease/iOS/navigation/)に基づいています。
iOS8に関するスクリーンショットは[一般公開されている情報](http://devstreaming.apple.com/videos/wwdc/2014/713xx1il4h4ur9c/713/713_whats_new_in_ios_notifications.pdf)から引用しています。)

#プッシュ通知での選択肢の作り方

![notificationCenteriOS8.png](https://qiita-image-store.s3.amazonaws.com/0/34488/6d682c79-570f-991b-d894-94ab1a55f275.png)

###手順
０. プッシュ通知の許可申請
１. プッシュ通知に表示するアクションを生成する
２. 生成したアクションをカテゴリに登録する
３. カテゴリをセッティングに登録する
４. セッティングをプッシュ通知に登録する

#プッシュ通知の許可申請
プッシュ通知を使用するにはユーザに許可画面を提示して、プッシュ通知を許可してもらう必要があります。

![pushTest.PNG](https://qiita-image-store.s3.amazonaws.com/0/34488/34ef8410-7868-ae53-c4f1-18920ed2aa00.png)

プッシュ通知を使用する多くのアプリがアプリの初回起動時に許可画面を表示しています。
しかし、それではプッシュ通知が何に使われるのか分からずユーザがメリットを感じられず、プッシュ通知を許可してくれないかもしれません。
アプリ内でプッシュ通知のメリットをユーザに提示し、その後に許可画面を表示すべきです。
<b>初回起動時のスプラッシュ画面で許可画面を表示することは必須ではありません。任意の場所で許可を求めることができます。</b>
許可画面は以下のコードが呼び出されたタイミングで表示されます。

```objc:Objective-C
[[UIApplication sharedApplication] registerForRemoteNotifications];
```

```swift:Swift
UIApplication.sharedApplication().registerForRemoteNotifications()
```

#プッシュ通知が利用可能かどうかの判断
許可申請画面を表示した後、サーバにリクエストを送り、プッシュ通知に必要なデバイストークンが登録されたかどうかがデリゲートメソッドに結果として返ってきます。
成功:didRegisterForRemoteNotificationsWithDeviceToken
失敗:didFailToRegisterForRemoteNotificationsWithError


```objc:Objective-C

- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // プッシュ通知が利用可能であればdeviceTokenが取得できる    
    NSLog(@"Device Token = %@", deviceToken);
}

- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    // プッシュ通知が利用不可であればerrorが返ってくる
    NSLog(@"error: %@", error);
}

```

```swift:Swift

func application(application: UIApplication!, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData!) {
    // プッシュ通知が利用可能であればdeviceTokenが取得できる
    NSLog("Device Token: " + "\(deviceToken)")
}
    
func application(application: UIApplication!, didFailToRegisterForRemoteNotificationsWithError error: NSError!) {
    // プッシュ通知が利用不可であればerrorが返ってくる
    NSLog("error: " + "\(error)")
}


```

#プッシュ通知の選択肢の作成
選択肢の動作を記述するアクションを生成します。
（選択肢のないプッシュ通知の場合、手順０の許可申請のみで十分です。）

アクションには以下の設定が可能です。
###アクションに設定可能な項目まとめ
| プロパティ                     | 機能                       |
|:------------------------------|:-----------------------------|
|.identifier|プログラム内での識別子             |
|.title　　　|選択肢に表示される文字|
|.activationMode|選択肢を選んだ時foregroundとbackgroundのどちらに行くか|
|.destructive|強調表示の有無|
|.authenticationRequired|選択時にパスコード入力の必要/不要|

##サンプルコード
###Objective-C

```objc:Objective-C

// --------------------------------------
// 1. プッシュ通知に表示するアクションを生成する
// --------------------------------------

// "承認(Accept)"の選択肢のためのアクションを作る
UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];

acceptAction.identifier = @"ACCEPT_IDENTIFIER";

// 画面に表示する文字の設定
acceptAction.title = @"Accept";

acceptAction.activationMode = UIUserNotificationActivationModeBackground;

// 強調表示の有無
acceptAction.destructive = NO;

// 承認させるときに認証を求めるか(NOの場合は不要)
acceptAction.authenticationRequired = NO;

// 同じ手順で"拒否(Decline)"のアクションを作る
UIMutableUserNotificationAction *declineAction = [[UIMutableUserNotificationAction alloc] init];
declineAction.identifier = @"DECLINE_IDENTIFIER";
declineAction.title = @"Decline";
declineAction.activationMode = UIUserNotificationActivationModeBackground;
declineAction.destructive = NO;
declineAction.authenticationRequired = NO;

// -----------------------------------
// 2. 生成したアクションをカテゴリに設定する
// -----------------------------------
UIMutableUserNotificationCategory *inviteCategory = [[UIMutableUserNotificationCategory alloc] init];

inviteCategory.identifier = @"INVITE_CATEGORY";

[inviteCategory setActions:@[acceptAction, declineAction]
    forContext:UIUserNotificationActionContextDefault];

// プッシュ通知に複数の選択肢が表示可能である
// 必ず見せたいActionはforContextにUIUserNotificationActionContextMinimalを設定する
[inviteCategory setActions:@[acceptAction, declineAction]
   forContext:UIUserNotificationActionContextMinimal];

// --------------------------------
// 3. セッティングへのカテゴリの設定する
// --------------------------------

// プッシュ通知のタイプを選択（バッジ、サウンド、アラート）
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;

//　タイプとカテゴリを設定
NSSet *categories = [NSSet setWithObject:inviteCategory];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];

// --------------------------------
// 4. セッティングをプッシュ通知に登録する
// --------------------------------
[UIApplication sharedApplication] registerUserNotificationSettings:settings];

```

###Swift

```swift:Swift

// --------------------------------------
// 1. プッシュ通知に表示するアクションを生成する
// --------------------------------------
var acceptAction = UIMutableUserNotificationAction()
        
acceptAction.identifier = "ACCEPT_IDENTIFIER"
        
// 画面に表示する文字の設定
acceptAction.title = "Accept"
        
acceptAction.activationMode = .Background

// 強調表示の有無
acceptAction.destructive = false
        
//  承認させるときに認証を求めるか(NOの場合は不要)
acceptAction.authenticationRequired = false
        
var declineAction = UIMutableUserNotificationAction()
declineAction.identifier = "DECLINE_IDENTIFIER"
declineAction.title = "Decline"
declineAction.activationMode = .Background
declineAction.destructive = false
declineAction.authenticationRequired = false
        
// -----------------------------------
// 2. 生成したアクションをカテゴリに設定する
// -----------------------------------
var inviteCategory = UIMutableUserNotificationCategory()
        
inviteCategory.identifier = "INVITE_CATEGORY"
        
inviteCategory.setActions([acceptAction, declineAction], forContext: .Default)
        
// プッシュ通知に複数の選択肢が表示可能である
// 必ず見せたいActionはforContextにMinimalを設定する
inviteCategory.setActions([acceptAction, declineAction], forContext: .Minimal)
        
// --------------------------------
// 3. セッティングへのカテゴリの設定する
// --------------------------------
        
// プッシュ通知のタイプを選択（バッジ、サウンド、アラート）
var types: UIUserNotificationType = .Badge | .Sound | .Alert
        
//　タイプとカテゴリを設定
var categories = NSSet(objects: inviteCategory)
var settings = UIUserNotificationSettings(forTypes: types, categories: categories)
        
// --------------------------------
// 4. セッティングをプッシュ通知に登録する
// --------------------------------
UIApplication().sharedApplication().registerUserNotificationSettings(settings)


```

#セッティングの登録後
セッティングをプッシュ通知に登録後、以下のデリゲートメソッドでどのタイプ（バッジ、サウンド、アラート）のプッシュ通知が許可されているかを判定できます。

```objc:Objective-C

- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    UIUserNotificationType allowedTypes = [notificationSettings types];
}

```

```swift:Swift
func application(application: UIApplication!, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings!) {
    var allowedType = notificationSettings
}
```

#サーバからのプッシュ通知
![APNs.001.jpg](https://qiita-image-store.s3.amazonaws.com/0/34488/ac1a8797-a2cb-fb38-f9a3-31447d9e6de5.jpeg)

ここからは実際にプッシュ通知をサーバから実行する際の処理についてまとめます。

生成した選択肢ありのプッシュ通知の動作を実行するために、プッシュ通知用のAPNsサーバから送るJSONの形式は以下のようになります。
categoryにidentifierで指定したものと同じIDを使用してください。
送信可能なJSONのサイズは最大2KBです。

```json:JSON
{
  "aps" : {
    "alert" : "You’re invited!",
    "category" : "INVITE_CATEGORY",
  }
}

```

#プッシュ通知への対応
アプリがプッシュ通知を受け取るとUIApplicationのデリゲートメソッドが反応します。
identifierのパラメータにAction.identifierで指定したIDが来るので、選んだ選択肢の動作を分岐させることができます。

```objc:Objective-C
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler {
    
    if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"]) {
        // "Accept"した時の処理
        ...
    }
    if ([identifier isEqualToString:@"DECLINE_IDENTIFIER"]) {
        // Declineした時の処理
        ...
    }

    // 終了時に呼ばれなければならない
    completionHandler();
}

```

```swift:Swift

func application(application: UIApplication!,
    handleActionWithIdentifier identifier: String!,
    forRemoteNotification userInfo: [NSObject : AnyObject]!,
    completionHandler completionHandler: (() -> Void)!) {

    if(identifier == "ACCEPT_IDENTIFIER") {
        // Acceptした時の処理
        ...
    }
    if(identifier == "DECLINE_IDENTIFIER") {
        // Declineした時の処理
        ...
    }
    
    // 終了時に呼ばれなければならない
    completionHandler()
}

```
#まとめ
iOS8から新規に追加されたプッシュ通知に選択肢を追加する方法をまとめました。
ユーザにプッシュ通知を送る際に提示する情報の幅が広がりました。
これからどのように新しいプッシュ通知を使ったアプリが出てくるのか楽しみです。



#参考
公式Class Reference
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/doc/uid/TP40006728-CH3-SW60