Helios

次にエントリを書くときは HBFav の次のバージョンの話、と思っていたのだが AppStore のレビューに時間がかかっているので、なんとなく閑話休題的に更新しておこう。

Helios について。ロゴがかわいい。

先月くらいに何かの拍子で自分の周囲でも話題になった。今年の4月くらいに Heroku からリリースされた、MBaaS (Mobile Backend as a Service) を構築するためのフレームワーク。実際には OSS なので Heroku からというか Heroku 社員の mattt さん によるもの。

mattt さんはご存知、iOS の AFNetworking や TTTAttributedLabel そのほかの開発者として有名なスーパーハッカーである。Heroku 勤務ということで、Heroku の親会社である Salesforce が開催の Salesforce Developer Conference Tokyo 2013 にも来ていた。ちなみにカンファレンスでは僕も登壇して、そのときの記事はここにある。はい宣伝。

もとい、Helios の話。

Backend as a Service

BaaS というのはまだ日本ではあまり馴染みのない言葉な気がするけど 2013 年は BaaS の年というかこれから盛り上がってくる分野で、市場規模の拡大も予測されている。この辺は次の資料、モバイルBaaSの概観と最新動向 が詳しい。

BaaS は "as a Service" という postfix のとおり、普通は SaaS 的な意味での「サービス」として提供されていて主要なプレイヤーは Facebook 傘下の Parse.com や最近人気が出てきた Kinvey など。最近は AWS がプッシュ通知基盤を提供するなどで徐々に参入してきて、いよいよ市場が勢いづいてきている感もある。

BaaS の提供する機能は IaaS/PaaS よりももっと上のレイヤで、例えば

  • クラウド側にあるオブジェクトストレージ ・・・ iOSSDK とかで作ったオブジェクトが自動でクラウド側で同期されて保存される的なもの
  • Twitter/Facebook 認証のバックエンド
  • プッシュ通知のバックエンド
  • In-App Purchase のバックエンド
  • 位置情報などのバックエンド

などなどである。これらのバックエンド機能を iOS/Android から簡単に利用できる SDK と、他プラットフォームからも利用するための JSON over HTTP な REST API がセットになっているというのがだいたいの構成。

いずれの機能もまあ自分で作ることもできなくはないのだけど、ちゃちゃっと作りたいときには面倒だったり、将来の運用を考えると自分でシステムを持ちたくない・・・という時に BaaS の出番。実際に使ってみると、例えば Parse.com のオブジェクトストレージは PFQuery と呼ばれる SQL ライクなクエリで探索できるスキーマレスDBみたいなデベロッパフレンドリな感じになってて、しかもそのオブジェクトストレージが他機能と有機的に連携してたりして非常に使い勝手が良く、病みつきになる部分がある。

HBFav の次のバージョンではプッシュ通知を実装したけど、そのバックエンドには Parse.com を使った。

Helios

Helios は、その Parse.com なんかが提供するような機能を、サードパーティのサービスを使うんではなくて、自分で構築したいという人のためのフレームワークHelios 自体は Ruby で書かれていて、SDK に相当する部分は mattt さんが別に作った iOS の各種ライブラリがそれに相当する。

Helios が提供する機能は

  • Data Synchronization
  • Push Notification
  • In-App Purchases
  • Passbook
  • Newsstand
  • Logging & Analytics

あたりで、上3つは特に BaaS の定番機能と言える。

Heliosフレームワークといっても、実際にはフレームワークというより BaaS の提供をわりとフルスタックで全部実装しちゃってる。それが rack アプリケーションとして実装されているから、sinatrarails で作った rack アプリに一緒に組み込んじゃって、突如 BaaS 機能を持たせるということができるようになっている。

そして、rack アプリケーションなので git push したら、ゼロコンフィグですぐに Heroku で動くようになっている。

この辺は実例を見た方がはやいと思う。

Helios 自身とかPostgreSQL とかその辺のインストールに関しては省略、helios コマンドでアプリケーションを作る。

% bundle exec heios new app
% cd app
% tree
.
├── Gemfile
├── Procfile
├── README.md
└── config.ru

0 directories, 5 files
% bundle

config.ru の中身は

require 'bundler'
Bundler.require

app = Helios::Application.new {
  service :data, model: Dir['*.xcdatamodel*'].first if Dir['*.xcdatamodel*'].any?
  service :push_notification
  service :in_app_purchase
  service :passbook
}

run app

こうなってる。rack アプリケーションを動かしてるだけ。

% bundle exec helios server

そして、これで立ち上がる。/admin で管理画面。

サーバーが立ち上がっている状態で、各機能のエンドポイントになる REST API の URL のルーティングが行われているので HTTP でそれら API を呼び出してバックエンド機能を利用する、という感じです。

プッシュ通知を実装する

試しにプッシュ通知を使ってみる。

iOS アプリから BaaS を利用してプッシュ通知を行う場合、iOS 側でプッシュ通知利用の設定を行う際に BaaS 側にデバイストークンを飛ばして登録し、BaaS はそのデバイストークンをパラメータに Apple のプッシュサーバーと通信して、端末へのプッシュ通知を依頼する形になる。

アプリ立ち上げ直後に helios のサーバーにプッシュ通知利用を設定するサンプルアプリは以下のようになる。例によって RubyMotion。

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    UIApplication.sharedApplication.registerForRemoteNotificationTypes(
      UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound
    )
    true
  end

  def application(application, didRegisterForRemoteNotificationsWithDeviceToken:deviceToken)
    serverURL = NSURL.URLWithString("http://XXXX.herokuapp.com/push_notification")
    orbiter = Orbiter.alloc.initWithBaseURL(serverURL, credential:nil)
    orbiter.registerDeviceToken(deviceToken,
      success: lambda { |responseObject|  NSLog("Registration Success: %@", responseObject) },
      failure: lambda { |error|  NSLog("Registration Error: %@", error) }
    )
  end

  def application(application, didFailToRegisterForRemoteNotificationsWithError:error)
    if error.code == 3010
      NSLog("Push notifications don't work in the simulator!")
    else
      NSLog("didFailToRegisterForRemoteNotificationsWithError: %@", error)
    end
  end
end

一見 REST を叩くコードが全く出てこないのでアレ? という感じであるけど、それはこの Helios との通信部分は Orbiter という、プッシュ通知登録用ライブラリがやってくれるから。これが他の BaaS でいうところの SDK 相当。

このアプリを実機に転送して立ち上げると helios と通信が行われて、helios 側のデータベースに端末のデバイストークンが登録される。実機転送にはプッシュ通知を有効にしたプロビジョニングプロファイル云々みたいなのがあるけど、まあそこは本筋じゃないのでここでは省略。

実機との通信は当然ローカルのサーバーでは試せないので heroku にデプロイしておく。Procfile そのほか heroku デプロイのための設定はあらかじめ用意されてるので

% heroku
% git push heroku master

とするだけ。

さて、これでプッシュ通知を送る準備はできたので、実際におくる・・・ わけだがその前に Apple サーバーにプッシュ通知を送りつける場合は、それが誰のどのアプリからの通信なのかを証明する証明書が必要。証明書はいつものように Apple Developer Center と Keychain を行き来しながら書き出す、その辺りのやり方は helios の README に載っているので割愛。

証明書を作ったら config.ru で push_notification 周りにちょいちょいと設定し、git に追加して Heroku に一緒に git push してやる。

require 'bundler'
Bundler.require

app = Helios::Application.new {
  service :data, model: Dir['*.xcdatamodel*'].first if Dir['*.xcdatamodel*'].any?
  service :push_notification, apn_certificate: './apple_push_notification.pem', apn_environment: 'development'
  service :in_app_purchase
  service :passbook
}

run app

ようやく準備が整ったので、端末にプッシュ通知を送るよう Helios に命令してみよう。この辺は素直に REST API で。

curl -X POST -d 'payload={"aps" : {"alert":"Hello, Helios!!"}}' http://xxxxx.herokuapp.com/push_notification/message

うまくいくと、こんな感じで端末に通知が届く。

上記は特にデバイスを指定せずに通知を送っているのでブロードキャストな通知を送る。デバイスを特定して通知したい場合は、POST のパラメータに token=….&payload=… とデバイストークンを指定するとよい。

こんな感じで Helios を使うと、プッシュ通知アプリが比較的簡単に作れる。この辺を自分ですべて実装するとなると Apple のプッシュ通知サーバーとの間の取り決めを守りながら自分でサーバーの実装を書かなければいけない (これが結構めんどくさい) し、そのインタフェースとなる API や管理画面やを作らなきゃいけないしで、難しくはないけれども、まあ手間はかかる。

Helios の中身

Helios が色々やってくれるのは分かった、それで、Helios の中は実際にどういう実装になっているのか。

この辺がプッシュ通知周りを扱うところの実装。URL のルーティングは Sinatra を使っていて、プッシュ通知を飛ばす箇所は housten という rubygems に丸投げになっている。また、デバイストークンの管理周りは rack/push_notification 任せ。ということで Helios 自身はじつはあんまりたいしたことをしてないのであった・・・

しかし「な〜んだ」、と思うことなかれ。実は先の Orbiter、この housten、rack/push_notification、Helios 実は全部同じ mattt さんが書いた実装なのである。つまり、Helios の実態はその mattt さんが実装した各種コンポーネントのグルー、という感じ。mattt さん、パワフルすぎるよね。

そんなわけか Helios そのものにはテストがなかったりするのだけど、プッシュ通知に限らず実際にはコアロジックは別のモジュールが担当していてそちらの部品でユニットテストが行われているようす。いや、テストなかったw Helios にどういうモジュールが組み込まれていて、それぞれがどういう役割を果たしているかは以下の記事が詳しい。

雑感

Helios のイントロダクションでした。以下雑感。

実際に使ってみると確かにお手軽なのだけど、Parse.com などを使い慣れているとちょっと機能が足りない。例えば Parse.com の場合、サーバー側に送ったデバイストークンと自分のアプリのユーザー情報を紐づけて保存しておくなどメタデータの管理が容易になっている。そしてそのメタデータを基に PFQuery でオブジェクトを取り出してプッシュ通知を送ったりとか、その辺のモデルストレージとAPIの連携がとても洗練されている。そういう部分が Helios にはない。

ただ、Helios は中身が Ruby でライトウェイトに実装されているのでカスタマイズも簡単に見える。また実行環境は Rack で疎結合なので、例えば Helios には認証の機能がないけれども、それは Rack ミドルウェアを導入することで補えるというか好きなものを選択できる、することが前提になっている。足りないところは自分で補え、ということなのだろうか。それともまだまだ完成してない、ということなのだろうか。

若干気になるのは4月にリリースされて、比較的若いプロジェクトなのだけど、2ヶ月前でアクティビティが止まっているところ。開発を続けるつもりなのかどうかちょっとわからない。一応、こないだの Salesforce のカンファレンスで matttt さん本人が Helios の話をしているし、停止ということはないのだろうけどまだ完成度がそれほどでもない段階でコミットがないのが何なのかはよくわからない。

先に見たとおり Helios 自身はグルーの役割を果たしているだけで重要な部分は他のコンポーネントが面倒を見ている。ので、Helios に満足できなかったら、それらのコンポーネントだけ拝借して思い通りのバックエンドを Helios を参考に SinatraRails あたりで実装してしまうというのも手かもしれない。そのためのリファレンスとしては優秀なプロジェクトだと思う。

BaaS の OSS も今後は盛り上がっていく、という見方もあるところなので個人的には頑張って継続してほしいなあと思っている ─ そう思うんだったらお前が Pull Request しろよ、という話にかならない昨今は生きていくのが大変です。