iOSのサーバサイドをMeteorにする


#始めに

なんというか[Meteorアドベントカレンダー出張版](http://qiita.com/advent-calendar/2014/meteor)みたいな感じです。
もともとBaaSである[firebase](https://www.firebase.com/)をサーバサイドでiOSアプリを作ろうとしたところで
push Notificationが無くて、[がっかりした](http://qiita.com/m0a/items/cf7a57face7a0e264881#spa%E4%BB%A5%E5%89%8D%E3%82%88%E3%82%8A%E5%AD%A6%E7%BF%92%E3%82%B3%E3%82%B9%E3%83%88%E3%81%8C%E5%A2%97%E5%8A%A0)ことがあるんですが、
Meteorならfirebaseっぽくサーバサイドを使えて(勝手に該当コレクションが永続化されている)
かつ、サーバサイドがカスタマイズ可能なのでpush Notificationが実装できるかなと思い試してみます。
[ここで作ってたやつ](http://qiita.com/m0a/items/b2b8f7f3fdcdcad37cf0)にpushNotificationを実装してみます。

# Meteorについて

MeteorとはリアルタイムWebアプリケーションフレームワークであり、サーバサイドとクライアントは
webSocket上に実装したDDPというプロトコルをベースに双方向通信を行っています。
[なんかイマイチという話題](http://mozaic.fm/post/100741841543/10-node-js-sideshow)も聞きますが、[今Node.js界で一番騒がれているフレームワークなようです。](http://qiita.com/oukayuka/items/14bfdcb6b5411a2b4b7c)

# 実装

ぶっちゃけ[ここ](http://qiita.com/m0a/items/b2b8f7f3fdcdcad37cf0)で終わっているんですが、
アプリが起動中にしか値の更新が行われないので、アプリが停止中でもpush Notificationで通知されるようにしてみようと思います。

参考：http://stackoverflow.com/questions/22598009/how-can-one-send-ios-push-notifications-from-a-meteor-app


まずはleaderboardを取得します。

```sh:console
meteor create --example leaderboard
cd leaderboard
```

nodeのモジュールを組み込めるようにパッケージを追加します。

```sh:console
meteor add meteorhacks:npm
```

プロジェクト直下に`packages.json`を作成します

```json:packages.json
{
  "apn": "1.6.2"
}
```

ここで`meteor`と叩くとパッケージの取り込みが行われて、再度`meteor`を実行するように要求されます
それとpush Notification用の証明書を作っておきます。
参考にしたサイトはこちら:
http://intink.blogspot.jp/2012/11/ios-nodejs.html
http://www.lancork.net/2013/08/how-to-ios-push-first/

上記情報を参考にcert.pemとkey.pemを作成し`private/`配下におきます。
`private/`以下はサーバからしかアクセスできなくなります。

`server/`ディレクトリを作成しpush notification用のコードを追加します。

```js:server/autorun.js

Meteor.startup(function(){
	//playersコレクション変更時に実行
    Players.find().observe(
        {
            'changed': function (oldDocument,newDocument) {
                console.log("player changed!!");
                Meteor.call("sendAppleNotifications");
            }
        }
    );

});

(function(){
    "use strict";
    var apn = Meteor.npmRequire("apn"),
        path = Npm.require('path'),
        apnOptions = Meteor.settings.apnOptions || {},
        alertSound = apnOptions.sound || "alert.aiff",
        apnConnection;

    // default apn connection options
    apnOptions = _.extend({
        cert: Assets.getText("cert.pem"), //Meteor独自 private/下のcert.pem
        key: Assets.getText("key.pem"),   //Meteor独自 private/下のkey.pem
        production:false //*.meteor.comに置くとデフォルトではproduction扱いになるようです
    }, apnOptions);
    apnConnection = new apn.Connection(apnOptions);


    var pushIds = ["0ccf880dfda2214560d1c3d3f0df67db11605fc27df1b1XXXXXXXXXX"]; //実機から取得。本来ならアクセス時に登録したものを取得すべきですよねー。
    var url = "testURL";
    var alertMessage = "alert";

    Meteor.methods({
        'sendAppleNotifications': function() {
            console.log("run sendAppleNotifications");
            var note = new apn.Notification();

            // expires 1 hour from now
            note.expiry = Math.floor(Date.now() / 1000) + 3600;
            note.sound = alertSound;
            note.alert = alertMessage;
            note.payload = {'url': url};
            note.badge=10;

            _.each(pushIds, function (token) {
                var device = new apn.Device(token);
                apnConnection.pushNotification(note, device);
                console.log("send notification!!!");
            });

            return {success: 'ok'}
        }
    });

})();

```


サーバサイドはautorunが動かないらしく、observeで監視して自動実行するようにしています。