プッシュ通知を究める！その①〜普通のプッシュ通知の実装の仕方〜


iOS10になってからプッシュ通知もいろいろ進化しました！各種プッシュ通知をどうやって実装するんですか？と聞かれることが多いのでまとめていきたいと思います。

以下予定

- iOS10からのプッシュ通知の実装の仕方（本稿）
- リッチ通知（メディア付き）の実装の仕方 -> [プッシュ通知を究める！その②〜リッチ通知（メディア付き）の実装の仕方〜](http://qiita.com/jollyjoester/private/70bc862c64384de8b082)
- リッチ通知（カスタムUI付き）の実装の仕方
- アクション付きプッシュ通知の実装の仕方

まずは普通のプッシュ通知の実装の仕方から

※証明書の作成などは以前から特に変わっていないので省きます。

検証環境：Xcode8.2.1, Swift3.0

# iOS10からのプッシュ通知の実装の仕方

iOS10からは`UserNotification.framework`を使えるようになりました。iOS9以前と比べるとちょっと変わった部分がありますのでまずはそれを復習していきましょう。

## プッシュ通知のCapabilitiesをONにする

まずはCapabilitiesにて「Push Notifications」を「ON」にします。
すると`プロジェクト名.entitlements`というファイルが生成されます。

![Screen_Shot_2017-01-12_at_2_42_43_PM.png](https://qiita-image-store.s3.amazonaws.com/0/8868/78e148d2-a251-f399-d7f0-e52a307600a4.png "Screen_Shot_2017-01-12_at_2_42_43_PM.png")

こいつがないと下記のエラーが出てプッシュ通知の登録ができません。

```
Remote Notification Error: no valid 'aps-environment' entitlement string found for application
```


## プッシュ通知の登録をする

`UserNotification.framework`をimportします。さらにプッシュ通知を有効にするために下記を実装します。

```AppDelegate.swift
import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        if #available (iOS 10.0, *) {
            let center = UNUserNotificationCenter.current()
            center.requestAuthorization(options: [.sound, .alert, .badge], completionHandler: {
                (granted, error) in
                
                if granted == true {
                    // プッシュ通知の登録		
                    application.registerForRemoteNotifications()
                } else {
                    // プッシュ通知を許可しなかった場合
                }
            })
        } else {
            // iOS9までのプッシュ通知のsetting
        }
        return true
    }
```

（余談：ここでは`didFinishLaunchingWithOptions`のタイミングで登録していますが、プッシュ通知の許可をしてもらいやすくするために別のタイミングで行うのもありです。別途どっかで書きます。）

初めて`requestAuthorization`が呼ばれた時には下記のようなプッシュ通知の許諾ダイアログが表示されます。

![IMG_9104.png](https://qiita-image-store.s3.amazonaws.com/0/8868/6c45ea5e-480d-3afe-6bbd-a5a205295e6d.png "IMG_9104.png")

こいつで「Allow」を押すと`granted`がtrueで渡ってきます。`granted`がtrueだったら`registerForRemoteNotifications()`を呼んでプッシュ通知の登録をします。

（余談：この分岐を書かなくても`granted`がfalseだった場合は`registerForRemoteNotifications()`を呼んでも後述するdeviceTokenの取得はできないようです。）

## deviceTokenを取得する

プッシュ通知の宛先となるdeviceTokenを取得するために下記のメソッドを実装します。

```AppDelegate.swift
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        
        let token = String(format: "%@", deviceToken as CVarArg) as String
        print("deviceToken: \(token)")
        // プッシュ通知送信サービスを使ってたらSDKにdeviceTokenセットしたりする
        
    }
```

`registerForRemoteNotifications()`が呼ばれると下記のメソッドが呼ばれます。引数としてdeviceTokenが渡ってくるのでこれを取得し、プッシュ通知を送信するのに用います。プッシュ通知を送信するサービスを使っていればここでそのサービスのSDKにdeviceTokenを渡したりします。

（余談：ネットワークが通じていないとここでdeviceTokenは取得できないようです。deviceTokenの取得は次回ネットワークが通じている時になります。deviceTokenの取得にはAPNsとの通信が必要なので当然ちゃ当然ですかね。）

## プッシュ通知を開いた時の処理を記述する（オプショナル）

プッシュ通知に関する`UNUserNotificationCenterDelegate`には下記２つのデリゲートメソッドがあります。

- `didReceive`を実装すると **プッシュ通知のバナーをタップしたり、通知センターの通知をタップしてアプリを起動した時** にこいつが呼ばれるようになります。`didReceive`というメソッド名ですがプッシュ通知を受け取った時ではなく、プッシュ通知からアプリが起動されたタイミングで呼ばれます。「プッシュ通知をタップしたら特定のページに遷移する」などをプッシュ通知からアプリを起動した時に特定の処理をしたい場合に用います（本当にプッシュ通知を「受け取った時」に何かを処理したい場合については別途どっかで書きます）。

- `willPresent`は **アプリがアクティブな状態（アプリがすでに開かれている状態）でプッシュ通知のバナーを表示したりしたい場合** にこいつを実装します（iOS9まではアプリがアクティブの状態で通知は表示できなかった）。`completionHandler([.badge, .sound, .alert])`に渡すOptionsによって通知の挙動を変えたりすることができます。

以下実装例です。

デリゲートの宣言＆デリゲートメソッドを実装します。

```AppDelegate.swift
extension AppDelegate: UNUserNotificationCenterDelegate {
    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        
        print("Push Notification will present \(notification)")
        completionHandler([.badge, .sound, .alert])
        
    }
    
    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        
        print("Push Notification did receive \(response)")
        completionHandler()
    }
}
```

デリゲートオブジェクトをセットします。`UNUserNotificationCenterDelegate`ファイルのコメント中に下記のようにあるので`applicationDidFinishLaunching`でデリゲートをセットしてやりましょう。

> The delegate must be set before the application returns from applicationDidFinishLaunching:.

```AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        if #available (iOS 10.0, *) {
            let center = UNUserNotificationCenter.current()
            
            center.delegate = self	// <-ここを追加
            
            center.requestAuthorization(options: [.sound, .alert, .badge], completionHandler: {
```

以上、何か間違い、質問等あればコメントによろしくお願いします！
次はリッチ通知について書く予定です。