APNs通知が送信出来た! その後にやること

はじめまして。株式会社ガラパゴスの宮田と申します。このたび、さくらのナレッジに寄稿させていただく事になりました。よろしくお願いいたします。

弊社は、スマートフォンアプリの受託開発を主な事業としております。スマートフォンアプリ開発業務ならではの、サーバやインフラに関するナレッジを、今後共有していけたらと思います。

通知は出来たけれども、、

本日のテーマは「APNs通知が送信出来た! その後にやること」です。
APNsは設定も面倒臭いですし、届いた時の感動も大きいので、正常系の送信が出来たところで満足して手を止めてしまいがちです。しかし運用してみると、色々なはまり所がありました。

1回の通信のパケット数が一定量を超えると切断される

LIGさんの記事にもあるのですが、一回の通知のパケットが一定量を超えると切断されます。どのくらいで切断されるかと言うと、公式に出ている数字はありません。この記事を書いている2014年2月頃だと10000byte程度で切断されているようです。

Invalidなトークンが混ざっていると、それ以降のトークンへ通知されない

100件の通知をしたとします。その中にInvalidなトークンが混在していると、それ以降の通知が届きません。Invalidのトークンとはサーバー側で指定しているAPNsの証明書とは別のアプリのトークンです。そもそもアプリが別のものだったり、同じアプリでもサーバー側はDistributionのAPNs証明書を使っているのに開発用のProvisioningProfileでビルドされたアプリから取得したトークンの場合もInvalidトークンになります。
リリース後も普通は実機で開発するわけですから、余程気を付けないと開発版のトークンは混在してしまうと思います。

ApnsPHPはどこまでやってくれるか

PHPでサーバー側の処理をするのであれば、ApnsPHPという便利なライブラリがあります。
このライブラリ経由で通知を行えば、前述の問題に対応してくれます。

  1. パケット量の問題で切断されたときに、自動で再接続して送信してくれる
  2. FeedbackServiceを叩いてアンインストールされたトークンを取得する

しかし、前述の2つの問題が同時に起こった場合に対処が出来ていないのです。つまり、途中にInvalidなトークンが混在していたがパケット量の問題で切断されると、APNsサーバーからInvalidなトークンの情報が取得出来ません。そのため、ApnsPHPの再送の機構も動きません。

こういった状況に対処するには、

  1. 100件ずつなど小分けにして送信する
  2. Invalidなトークンを出来る限り消す

などの対策が取れます。

Invalidなトークンを消す方法

Invalidなトークンを含んだApnsPHPの送信の1セッションが正常に終わると、何番目のトークンがInvalidだったのかを取得する事が出来ます。この情報を見て、こまめにトークンのDBに無効フラグを立てて行き、次回以降は送信しないようにロジックを修正すると良いです。以下がソースコードの例になります。

// Instanciate a new ApnsPHP_Push object
$push = new \ApnsPHP_Push(
    1, //SANDBOXかどうかのフラグ
    "config/notification/apns_dev.pem"//APNsで使うpemファイルのパス
);
$push->setProviderCertificatePassphrase('hogehoge');//パスフレーズ

$push->connect();

foreach($devices as $device) { //$devicesはトークンの配列
    $message = new \ApnsPHP_Message($device);
    $message->setText($text);
    $message->setExpiry(60*60*24*7);
    $push->add($message);
}

$push->send();
$push->disconnect();

$aErrorQueue = $push->getErrors(); //エラーの取得
if (!empty($aErrorQueue)) {
    foreach($aErrorQueue as $info) {
        if(isset($info['ERRORS'])) {
            foreach($info['ERRORS'] as $error) {
                if(isset($error['statusMessage']) && $error['statusMessage'] == 'Invalid token') {
                    $invalid_tokens[] = $devices[$error['identifier']-1];
                }
            }
        }
    }
    /*
     * InvalidなトークンをDBから除去する処理
     */
}

最後に

上記記事は現時点での経験則に過ぎなかったりもするので、そもそも間違っていたり、APNsの仕様が変わることもあると思います。その時は修正したいと思いますので、yoshiroh.miyata at_mark gmail.com までご連絡下さい。

おしらせ