[Swift]AWS SNSでiOSにPush通知する

# 概要
新卒エンジニア4ヶ月目あたり。
学生時代はJava・PHPを勉強し、会社に入ってからSwiftを触り始めました。
案件でAWSからpush通知を行ったので備忘録としてまとめてみる。

# 流れ
大まかな流れはこんな感じ。

1. プッシュ通知用のCertificateの作成
2. AWS上の設定（SNS, Cognito）
3. AWS SDKの実装
4. プッシュ通知の送信（AWS）

# プッシュ通知用のCertificateの作成
プッシュ通知用のP12ファイルが必要になりますが、
今回は自社用のものが用意されていたため省きます。
知りたい方は[こちら](https://qiita.com/ijun/items/2cbff7664e49fb93bf39)を参考にしてください。

# AWS上の設定（SNS, Cognito）

## SNS（Simple Notification Service）の設定

リージョンを東京に変更しSNSサービスを開く
その後左のメニューからアプリケーションを選択

![aws2.png](https://qiita-image-store.s3.amazonaws.com/0/266735/34ae1110-a488-467a-d343-2b8f366c176e.png)

プラットフォームアプリケーションの作成をクリック
アプリケーション名とプッシュ通知プラットフォームを選択
(赤枠がiOSで黄枠がAndroid)

![aws3.png](https://qiita-image-store.s3.amazonaws.com/0/266735/86ad8886-e678-f304-c3f8-7f7e70e820d5.png)

作成してある証明書をここで選択しプラットフォームを作成します。
※Windowsのせいか変なところにファイル選択がありますが気にしないでください

![aws4.png](https://qiita-image-store.s3.amazonaws.com/0/266735/e4a4623d-de7d-b8c1-751a-60f2b2b63b42.png)

## AWS Cognitoの設定

Cognitoサービスを選択し、IDプール名を設定。
今回はテストも行ったため認証されていないIDに対してもアクセスを有効にします。
設定が終わればプールを作成します。

![cognite1.png](https://qiita-image-store.s3.amazonaws.com/0/266735/4f1bb78e-3203-4fae-ce7f-68ead59d457d.png)

ここはIAMのロール（権限）を設定できますが、今回は特にいじらないためそのまま許可します。

![cognite2.png](https://qiita-image-store.s3.amazonaws.com/0/266735/63eb5d3e-8d61-a419-fc43-839c3caa6af1.png)

使用するプラットフォームを選択できます。
また選択したプラットフォームにあったコードも書かれています。
![cognite3.png](https://qiita-image-store.s3.amazonaws.com/0/266735/30875984-93a9-1590-cfbf-f85b3dc70eb3.png)


# AWS SDKの実装
コードに関しては[こちら](https://qiita.com/akira-sasaki/items/4f97b72cba4f3613d853)の方のを記載させていただいております。

### CocoaPod

```Pod:Podfile
    pod 'AWSSNS'
    pod 'AWSCognito'
```

### import  

```Swift
    import CoreData
    import AWSCognito
    import AWSSNS
```

### push通知の許可

```Swift:AppDelegate.swift
 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.

        // ユーザからPush Notification通知の許可を取得する
        let types: UIUserNotificationType = [UIUserNotificationType.Badge, UIUserNotificationType.Alert, UIUserNotificationType.Sound]
        let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)

        application.registerUserNotificationSettings(settings)
        application.registerForRemoteNotifications()

        return true
    }
```

### Tokenの登録

```Swift:AppDelegate.swift
    func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData ) {

        let characterSet: NSCharacterSet = NSCharacterSet(charactersInString: "<>")

        // Device Tokenの取得
        let deviceTokenString: String = (deviceToken.description as NSString)
            .stringByTrimmingCharactersInSet( characterSet )
            .stringByReplacingOccurrencesOfString(" ", withString: "") as String

        // Initialize the Amazon Cognito credentials provider
        let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.APNortheast1,
                                                                identityPoolId:"Identity Pool ID")
        let configuration = AWSServiceConfiguration(region:.APNortheast1, credentialsProvider:credentialsProvider)
        AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration

        let sns = AWSSNS.defaultSNS()
        let request = AWSSNSCreatePlatformEndpointInput()
        request.token = deviceTokenString
        request.platformApplicationArn = "Application Arn"
        request.customUserData = "TEST"

        sns.createPlatformEndpoint(request).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task: AWSTask!) -> AnyObject! in
            if task.error != nil {
                print("Error: \(task.error)")
            } else {
                let result = task.result as! AWSSNSCreateEndpointResponse
                let subscribeInput = AWSSNSSubscribeInput()
                subscribeInput.topicArn = "Topic Arn"
                subscribeInput.endpoint = result.endpointArn
                subscribeInput.protocols = "Application"
                sns.subscribe(subscribeInput)

                self.saveEndpointArn(result.endpointArn)
            }
            return nil
        })
}
```

### pushの受け取り

```Swift:AppDelegate.swift
// プッシュを受け取ったとき
    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        let appState: UIApplicationState = application.applicationState;
        var appStateString: String = "";
        if (appState == .Active) {
            appStateString = "active";
        } else if (appState == .Inactive) {
            appStateString = "inactive";
        } else if (appState == .Background) {
            appStateString = "background";
        }

        let notifiAlert = UIAlertView()
        if let aps = userInfo["aps"] as? NSDictionary {
            let msg: NSString = aps["alert"] as! NSString
        }
    }
```
#まとめ
AWSのUIが定期的に変わっているためかWeb上の記事で分かりにくいことが多くあったなーってかんじ。

初めて記事を書いてみたので、まとめることがとても難しかった。。
今後も定期的に記事を書いていきたいと思っている（願望）

# 参考記事
- [AWS CognitoとAWS SNSでDBレスで構築するiOS向けPush Notification](https://qiita.com/akira-sasaki/items/4f97b72cba4f3613d853)
- [Amazon SNS で、iOS・Androidにpush通知する方法](https://qiita.com/papettoTV/items/f45f75ce00157f87e41a)
- [AWS SNSを使ってiOSへpush通知](https://qiita.com/ijun/items/2cbff7664e49fb93bf39)