Xcode6でiOS8へPush通知が送れない場合の解決方法


# はじめに

iOS8からプッシュ通知の通知領域をカスタマイズ可能になったり、ボタンを表示する機能が追加されました。
(こちらについては、他の投稿に詳しくありますので本記事では割愛します。)

機能追加に伴い、プッシュ通知の設定方法や、デバイストークンの取得方法に変更がありましたので、そちらについて説明していこうと思います。

__本記事はXcode6(iOS8ビルド)でビルドされたのiOS8の端末での動作となります。
Xcode5(iOS7ビルド)の場合はいままでの設定方法でiOS8へプッシュ通知が送信できる事を確認しています。__

## プッシュ通知の設定

iOS7以前では`-application:didFinishLaunchingWithOptions:`へ下記の様な`registerForRemoteNotificationTypes:`でプッシュ設定を行うのが一般的でした。
しかし、iOS8からこのメソッドがdeprecatedとなり、新しく`registerUserNotificationSettings:`というメソッドを使用するようになりました。

* [-registerForRemoteNotificationTypes:](https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instm/UIApplication/registerUserNotificationSettings:)
* [-registerUserNotificationSettings:](https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instm/UIApplication/registerUserNotificationSettings:)

### iOS7以前

```objectivec:ApplicationDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	[application registerForRemoteNotificationTypes:
		UIRemoteNotificationTypeAlert|
		UIRemoteNotificationTypeBadge|
		UIRemoteNotificationTypeSound];
}
```

### iOS8以降

```objectivec:ApplicationDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIUserNotificationType types =
                        UIUserNotificationTypeBadge|
                        UIUserNotificationTypeSound|
                        UIUserNotificationTypeAlert;
    
    UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
    
    [application registerUserNotificationSettings:mySettings];
}
```

__iOS7以前をサポートする場合、上記の2つの処理を同居させる必要があるため、OSバージョン判定による処理の分岐が必要となります。__

## デバイストークンの取得

iOS7以前では上記のメソッド実行時に、正常に設定が完了した場合、`-application:didRegisterForRemoteNotificationsWithDeviceToken:`からデバイストークンが取得可能でしたが、iOS8からはその間に1手間必要となりました。

プッシュ通知のSettingsを登録後、`-application:didRegisterUserNotificationSettings:`メソッドがコールされ、そこから明示的に`-registerForRemoteNotifications`を呼び出さないとデバイストークンの取得が完了されません。

```objectivec:ApplicationDelegate.m
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    [application registerForRemoteNotifications];
}
```

* [-registerUserNotificationSettings](https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instm/UIApplication/registerForRemoteNotifications)

上記のように、iOS8ではデバイストークンの取得に1段階必要な処理が増えています。

* iOS7
`-registerForRemoteNotificationTypes:`
↓
`-application:didRegisterForRemoteNotificationsWithDeviceToken:`

* iOS8
`-registerUserNotificationSettings:`
↓
`-application:didRegisterUserNotificationSettings:`
↓
`-application:didRegisterForRemoteNotificationsWithDeviceToken:`

# おわりに

新しいXcode,新しいiOSが発表される度に、この手の問題に悩まされます。
Xcodeをバージョンアップした際はWarningなどを注意して見ればアプリを公開してしまう前に問題に対処できるかもしれません。

# 参考資料
* [UIApplication Class Reference](https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIApplication_Class/index.html)
* [iOS8からのプッシュ通知](http://qiita.com/FumitakaYamada/items/c394cc991d6f863b56fa)