VaporAPNSを使ってプッシュ通知を送る

APIサーバーをSwiftで構築できるようになった今、プッシュ通知用のサーバーもSwiftで構築したい！ということがあるかもしれません。そこで「[VaporAPNS](https://github.com/matthijs2704/vapor-apns)」です。

この記事ではVaporAPNSを使ってプッシュ通知を送る方法を紹介します。実際はデバイストークンを受け取ってDBに保存したりしますが、その部分は省きデバイストークンが分かっている前提で紹介します。デバイストークンを受け取るサーバー自体もVaporで簡単に実装できるのでぜひ試してみてください。

## 環境

以下の環境で検証しました。

- OS X El Capitan 10.11.5
- Swift 3.0.1
- VaporAPNS 1.1.1
- iOS 10.1

## 準備

### HTTP/2

APNs Provider APIを利用するためにHTTP/2に対応したクライアントが必要なため以下のようにします。

```bash
$ brew reinstall curl --with-openssl --with-nghttp2
$ brew link curl --force
```

### 証明書

APNsとの通信に使用する証明書を準備します。証明書の作成方法は検索すれば沢山ヒットするため省きます。新しくAuthentication Keyを使って認証する方式もありますが、そちらについてはこの記事では触れません。検証してはいませんが、VaporAPNSのドキュメントには、Authentication Keyを使った方法についても触れているので確認してみてください。

従来方式を使用する場合は以下のようにしてp12ファイルから証明書と秘密鍵を取り出してください。

```bash
$ openssl pkcs12 -in Certificates.p12 -out push.crt.pem -clcerts -nokeys
$ openssl pkcs12 -in Certificates.p12 -out push.key.pem -nocerts -nodes
```

## 導入

まずはSwift Package Managerでパッケージを作成します。今回は「vapor-apns」というディレクトリに作成しています。

```bash
$ swift package init --type executable
```

次にPackage.swiftにライブラリの依存関係を記述します。

```Swift
import PackageDescription

let package = Package(
    name: "vapor-apns",
    dependencies: [
        .Package(url:"https://github.com/matthijs2704/vapor-apns.git", majorVersion: 1, minor: 1)
    ]
)
```

フェッチします。

```bash
$ swift package fetch
```

次に必須ではないですがXcodeのプロジェクトファイルを生成します。Xcodeを使うと普段アプリを開発している環境でサーバーも実装できるので便利です。

```bash
$ swift package generate-xcodeproj
```

## 実装

次にVaporAPNSを使って実際にプッシュ通知を受け取る実装をします。main.swiftに

```Swift
import VaporAPNS

let devicetToken = "デバイストークン"

let options = try! Options(topic: "バンドルID", certPath: "証明書のパス", keyPath: "秘密鍵のパス")
let vaporAPNS = try VaporAPNS(options: options)
let payload = Payload(message: "hoge")
let pushMessage = ApplePushMessage(priority: .immediately, payload: payload)

vaporAPNS.send(pushMessage, to: devicetToken)

```

こんな感じのコードを実装します。アプリ側では、

```Swift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        let center = UNUserNotificationCenter.current()
        center.delegate = self
        center.requestAuthorization(options: [.badge, .sound, .alert]){ isGranded, _ in

            if isGranded {
                application.registerForRemoteNotifications()
            }
        }
        
        return true
    }
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        
        let characterSet = CharacterSet(charactersIn: "<>")
        let deviceToken = String(format: "%@", deviceToken as CVarArg)
            .trimmingCharacters(in: characterSet)
            .replacingOccurrences(of: " ", with: "")
        
        print("deviceToken: \(deviceToken)")
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        
        completionHandler([.badge, .sound, .alert])
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        completionHandler()
    }
}
```

こんな感じに実装して、端末にインストールします。

あとはコンパイルして実行するだけです。

```bash
$ swift build
$ .build/debug/vapor-apns
```

## 結果

<img src="https://blog.reb-dev.com/wp-content/uploads/2016/12/スクリーンショット-2016-12-11-14.18.47.png" width="50%">


やりました。


## 参考
- [iOSのプッシュ通知送信時にトークン認証が使えるようになったので調べてみた](http://qiita.com/Tueno@github/items/9b4b59a9191b64299cf8)