[iOS] テスト用にミニマムにPUSH通知できる仕組みを作る


PUSH通知はサーバが必要で、さらに証明書やAPNsなどちょっとしたモックを作成するには大掛かりになるのでやってなかったのですが、検証やテストのためにミニマムな構成が作れると捗ると思ったのでざっと調べて構築してみました。

あくまでモックを作る程度のものなので、デバイストークンを直に書いて動かすなど色々必要なことを端折ってます。

今回の開発環境構築はこちらの記事（[iOSアプリのPush通知の実装方法](http://qiita.com/sxo/items/eab3e2bc9febb57cf37f)）を参考にさせていただきました。

-------------------------------------------

## 実装フロー

冒頭でも書いた通り、PUSH通知を実現するにはクライアントサイドだけでなく、いくつかの準備が必要になります。
大まかな実装フローは以下になります。

1. PUSH通知を可能にするプロビジニングプロファイルを準備する
2. クライアントサイドにてAPNsにデバイスを登録する
3. サーバサイドを実装して実際にPUSH通知を送る


## プロビジニングプロファイルの準備

まずはプロビジニングプロファイルを準備します。
デバイス登録などの仕方については割愛します。
ある程度実機で実装を行ったことがあることを前提に書いています。

### AppIDにPush Notificationの許可を設定する

AppID登録画面（あるいは編集画面）にて、「App Services（Enable Service）」欄から「Push Notification」にチェックを入れて有効にします。
チェックを入れると「Development SSL Certificate」と「Production SSL Certificate」という項目が現れるので、今回は「Development SSL Certificate」のほうから設定を進めます。

#### APNs用に証明書を登録する

「Development SSL Certificate」の項目内に「Create Certificate」ボタンがあるのでそれをクリックし、証明書の登録手順に進みます。
ここでキーチェーンから作成した自身の証明書を登録します。（手順に関してはサイトにしっかりと記載があるのでそれにしたがって登録すればOKです）

### プロビジニングプロファイルを設定する

プロビジニングプロファイルの項目に移動し、新規作成 or 編集画面にて、上記で設定したAppIDの指定と証明書の指定、実行するデバイスUUIDを登録し、プロビジニングプロファイルを作成します。
作成後、プロビジニングプロファイルをダウンロードしそれをインストールします。

### PUSHに必要な証明書ファイル（pemファイル）を用意する

キーチェーンに登録されている証明書をexportすることでこれを準備します。
まずはキーチェーンを開き、「Apple Development IOS Push Services:(AppID)」と書かれているものを見つけます。
そしてその項目を開き「証明書」と「秘密鍵」の両方を選択します。

↓こんな感じ
![証明書イメージ](https://qiita-image-store.s3.amazonaws.com/0/5616/b13af9da-1ed0-5703-fef9-53feba84b4dd.png "証明書イメージ")

選択した状態で右クリックし「2個を書き出す」を選択し、`p12` 形式でexportします。

最後に以下のコマンドをターミナルで実行します。

```console
openssl pkcs12 -in anyName.p12 -out anyName.pem -nodes
```

`anyName` の部分はexportしたファイル名を指定してください。 `-out` で指定するファイル名も任意です。


## クライアントサイドでAPNsへの登録を実行する

### Inof.plist（プロジェクト設定）をRemote Notification可能にする

以下のように、Xcodeプロジェクトの「Capabilities」設定を変更します。
（この処理は内部的にはInfo.plistに決められた項目を自動的に追加します）

![Xcode画面](https://qiita-image-store.s3.amazonaws.com/0/5616/11811063-be19-a85d-749e-3d518f9f0f18.png "Xcode画面")

### アプリ起動時にPUSH通知設定の許可ダイアログを出す

AppDelegate内に以下の記述をしてPUSH通知の許可を求めるダイアログを表示します。

```objc
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [application unregisterForRemoteNotifications];
    [application registerForRemoteNotifications];
    
    UIUserNotificationType types = (UIUserNotificationTypeBadge |
                                    UIUserNotificationTypeSound |
                                    UIUserNotificationTypeAlert);
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
    [application registerUserNotificationSettings:settings];
    
    return YES;
}
```

ポイントは `UIUserNotificationType` を指定する点です。
iOS7以前はこれとは違った指定方法のようですが、iOS8以降は上記の指定になります。

この処理を書かないといわゆるバナーやダイアログで表示されるPUSH通知が表示されません。（同時に、通知設定画面にも項目が現れません）

これを実行することでPUSH通知を許可するかのダイアログが表示されるようになります。


### デバイストークンを受け取る

最後に、APNsに登録した際に発行されるデバイストークンを取得します。

```objc
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSString *token = deviceToken.description;
    token = [token stringByReplacingOccurrencesOfString:@"<" withString:@""];
    token = [token stringByReplacingOccurrencesOfString:@">" withString:@""];
    token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    NSLog(@"deviceToken: %@", token);
}
```

取得すると上記のメソッドが自動的に呼ばれるので、そこからデバイストークを整理して取り出します。
（`<aaaa bbbb cccc....>` という形で渡されるので `<` `>` ` `（空白） を取り除く）

今回はあくまでテスト目的で実装しているので、このデバイストークンをコピペして、次のサーバサイドの処理でそれをそのまま利用します。

実際にサーバを立てて運用する場合はこのトークンと該当ユーザをひも付けて管理する必要があります。


## サーバサイドを実装する

今回はこちらの記事（[RubyでiPhoneアプリにPush通知](http://qiita.com/nyamage/items/15918ffb82c7dc517ae8)）を参考に、Rubyでサーバサイド側の処理を書きました。
とても便利なライブラリが提供されていて、十数行書くだけでPUSH通知を送ることができました。

実際に書いたコードは以下です。

```ruby
require 'grocer'

pusher = Grocer.pusher(
    certificate: "/path/to/push_development.pem",
    passphrase: "",
    gateway: "gateway.sandbox.push.apple.com",
    port: 2195,
    retries: 3
)

notification = Grocer::Notification.new(
    device_token: "取得したデバイストークン",
    alert: "Hello from Grocer!",
    badge: 42,
    category: "a category",
    sound: "siren.aiff",
    expiry: Time.now + 60 * 60,
    identifier: 1234,
    content_available: true
)

pusher.push(notification)
```

以上で終わりです。

ここまでの準備が終わったら、あとは最後に記載したRubyのコードを実行すると手元の端末にPUSHが飛んでくることが確認できると思います。

テスト目的であれば[外部サーバでそうした登録やPUSH通知を行う仕組み](http://qiita.com/griffin_stewie/items/baf6782a5e363a091178)があるようなので、もっと幅広いテストもできそうです。

が、とりあえず今回は手元でPUSHを送ってデバッグできればいいだけだったのでここまでの実装にしました。